home *** CD-ROM | disk | FTP | other *** search
/ CD ROM Paradise Collection 4 / CD ROM Paradise Collection 4 1995 Nov.iso / program / ogrid100.zip / GLTSHEET.PAS < prev    next >
Pascal/Delphi Source File  |  1994-12-29  |  162KB  |  4,996 lines

  1. {********************************************************************
  2.  
  3.   OOGrid Library(TM) v1.0 for Borland/Turbo Pascal (Real Mode/TV)
  4.   Copyright (C) 1994 by Arturo J. Monge
  5.   Portions Copyright (C) 1989,1990 Borland International, Inc.
  6.  
  7.   OOGrid Library(TM) Main Unit:
  8.     Implementation of a simple spreadsheet.
  9.  
  10.   Copyright (C) 1994 by Arturo J. Monge
  11.  
  12.   Last Modification : December 29th, 1994
  13.  
  14. *********************************************************************}
  15.  
  16. {$F+,O+,N+,E+,X+}
  17.  
  18. unit GLTSheet;
  19.  
  20. {****************************************************************************}
  21.                                  interface
  22. {****************************************************************************}
  23.  
  24. uses Crt, Dos, Objects, Views, Drivers, TCHash, GLSort, GLParser, GLSupprt,
  25.      GLCell, GLViews, GLEquate;
  26.  
  27. const
  28.  
  29. { Constants used by TSpreadSheet's methods }
  30.  
  31.   RedrawYes = True;
  32.   RedrawNo  = False;
  33.   EditYes = True;
  34.   EditNo = False;
  35.   DisplayYes = True;
  36.   DisplayNo = False;
  37.   ModifiedYes = True;
  38.   ModifiedNo = False;
  39.   RemoveBlock = True;
  40.   RemoveSingleCell = False;
  41.   ChangeYes = True;
  42.   ChangeNo = False;
  43.  
  44. const
  45.  
  46. { TSpreadSheet palette }
  47.  
  48.  CSpreadSheet = #12#13#14#15#16#17#18#19#20#21#22#23#24#25#26#27#28#29#30+
  49.                 #31#32#33#34#35#36;
  50.  
  51. { CSpreadSheet palette layout }
  52.  
  53.   { 1 = Empty Cell }
  54.   { 2 = Value Cell }
  55.   { 3 = Text Cell }
  56.   { 4 = Repeat Cell }
  57.   { 5 = Formula Cell }
  58.   { 6 = Column headers }
  59.   { 7 = Row numbers }
  60.   { 8 = Cell Data Area }
  61.   { 9 = Cell Contents Area }
  62.   { 10 = Spreadsheet Info Area }
  63.   { 11 = Cell In Block }
  64.   { 12 = Cell Highlighted }
  65.   { 13 = Cell Highlighted in Block }
  66.   { 14 = Unlocked Cell }
  67.   { 15 = Unlocked Cell in Block }
  68.   { 16 = Unlocked Cell Highlighted }
  69.   { 17 = Unlocked Cell Highlighted in Block }
  70.   { 18 = Cell Error }
  71.   { 19 = Cell Error in Block }
  72.   { 20 = Cell Error Highlighted }
  73.   { 21 = Cell Error Highlighted in Block }
  74.   { 22 = Unlocked Cell Error }
  75.   { 23 = Unlocked Cell Error in Block }
  76.   { 24 = Unlocked Cell Error Highlighted }
  77.   { 25 = Unlocked Cell Error Highlighted in Block }
  78.  
  79. type
  80.    PColStart = ^ColStartArray;
  81.    ColStartArray = array[0..ScreenCols] of Byte;
  82.    { Array used to store the screen positions where displayed columns start }
  83.  
  84.    PSpreadSheet = ^TSpreadSheet;
  85.    TSpreadSheet = object(TScroller)
  86.          Number               : Byte;
  87.          Modified             : Boolean;
  88.          MaxDecimalPlaces     : Byte;
  89.          DefaultColWidth      : Byte;
  90.          DefaultDecimalPlaces : Byte;
  91.          DefaultCurrency      : CurrencyStr;
  92.          MaxRows              : Integer;
  93.          MaxCols              : Integer;
  94.          MaxColWidth          : Byte;
  95.          MaxScreenCols        : Byte;
  96.          TotalRows            : ScreenRowRange;
  97.          RowNumberSpace       : Byte;
  98.          OldCurrPos           : CellPos;
  99.          CurrPos              : CellPos;
  100.          LastPos              : CellPos;
  101.          ScreenBlock          : PBlock;
  102.          CurrBlock            : PBlock;
  103.          BlockOn              : Boolean;
  104.          ColArea              : TScreenArea;
  105.          RowArea              : TScreenArea;
  106.          InfoArea             : TScreenArea;
  107.          DataArea             : TScreenArea;
  108.          DisplayArea          : TScreenArea;
  109.          ContentsArea         : TScreenArea;
  110.          BlankArea            : TScreenArea;
  111.          NoBlankArea          : Boolean;
  112.          ColStart             : PColStart;
  113.          CellHash             : TCellHashTable;
  114.          WidthHash            : TWidthHashTable;
  115.          OverwriteHash        : TOverwriteHashTable;
  116.          FormatHash           : TFormatHashTable;
  117.          DisplayFormulas      : Boolean;
  118.          AutoCalc             : Boolean;
  119.          GoToEnd              : Boolean;
  120.          KeyPressed           : Boolean;
  121.          EmptyRowsAtTop       : Byte;
  122.          EmptyRowsAtBottom    : Byte;
  123.          SheetProtected       : Boolean;
  124.          DisplayHeaders         : Boolean;
  125.          UnlockedHash         : TUnlockedHashTable;
  126.          ColHeadersHash       : THeadersHashTable;
  127.       constructor Init(var Bounds: TRect; InitCells: LongInt;
  128.                            AEmptyRowsAtTop, AEmptyRowsAtBottom: Byte;
  129.                            AHScrollBar, AVScrollBar: PLimScrollBar;
  130.                            AInitMaxCols, AInitMaxRows: Integer;
  131.                            InitDefaultColWidth,
  132.                            InitDefaultDecimalPlaces,
  133.                            InitMaxDecimalPlaces: Byte;
  134.                            InitDefaultCurrency: CurrencyStr);
  135.       function  AddCell(CellType: CellTypes; Pos: CellPos; Error: Boolean;
  136.                   Value: Extended; Input: String): Boolean; virtual;
  137.       function  CellHashStart(TotalCells: LongInt): BucketRange; virtual;
  138.       function  CellsProtected(Block: TBlock): Boolean; virtual;
  139.       function  CellToFString(P: CellPos; var AColor: Byte): String; virtual;
  140.       procedure ChangeBounds(var Bounds: TRect); virtual;
  141.       procedure ChangeColHeaders; virtual;
  142.       procedure ChangeColWidth; virtual;
  143.       procedure CheckForDragging; virtual;
  144.       procedure ClearCurrBlock; virtual;
  145.       procedure ClearScreenArea(AreaToClear: PScreenArea); virtual;
  146.       function  ColHeadersHashStart : BucketRange; virtual;
  147.       function  ColumnToString(Column: Word): String; virtual;
  148.       function  ColToX(Col: Integer): Byte; virtual;
  149.       function  ColWidth(Col: Integer): Byte; virtual;
  150.       procedure CopyCellBlock; virtual;
  151.       procedure DeleteBlock(Block: TBlock; var Deleted: Boolean); virtual;
  152.       procedure DeleteCell(Pos: CellPos; var Deleted: Boolean); virtual;
  153.       procedure DeleteColFromHash(Block: TBlock;
  154.                   Columns, EndDelCol: Word; var Deleted: Boolean); virtual;
  155.       procedure DeleteColHeaders(Block: PBlock); virtual;
  156.       procedure DeleteColumns; virtual;
  157.       procedure DeleteRowFromHash(Block: TBlock;
  158.                   Rows, EndDelRow: Word; var Deleted: Boolean); virtual;
  159.       procedure DeleteRows; virtual;
  160.       procedure DisplayAllCells; virtual;
  161.       procedure DisplayBlankArea; virtual;
  162.       procedure DisplayBlock(B: TBlock); virtual;
  163.       procedure DisplayBlockDiff(B1, B2: TBlock); virtual;
  164.       procedure DisplayCell(P: CellPos); virtual;
  165.       procedure DisplayCellBlock(C1, R1, C2, R2: Word); virtual;
  166.       procedure DisplayCellData; virtual;
  167.       procedure DisplayCols; virtual;
  168.       procedure DisplayInfo; virtual;
  169.       procedure DisplayRows; virtual;
  170.       procedure DoAfterEndInput; virtual;
  171.       procedure DragCursorWithMouse(Event: TEvent); virtual;
  172.       procedure Draw; virtual;
  173.       procedure EraseCellBlock(EraseBlock: Boolean); virtual;
  174.       procedure ExtendCurrBlock(Redraw : Boolean); virtual;
  175.       procedure FindLastPos(DPos: CellPos); virtual;
  176.       procedure FindScreenColStart; virtual;
  177.       procedure FindScreenColStop; virtual;
  178.       procedure FindScreenRowStart; virtual;
  179.       procedure FindScreenRowStop; virtual;
  180.       procedure FixBlockOverWrite(Block: TBlock); virtual;
  181.       function  FixOverWrite: Boolean; virtual;
  182.       procedure FormatDefault; virtual;
  183.       function  FStringSituationColor(P: CellPos; var CP: PCell;
  184.                   var HasError, ColorFound: Boolean): Byte; virtual;
  185.       procedure GetFormat; virtual;
  186.       function  GetPalette: PPalette; virtual;
  187.       procedure GoToCell; virtual;
  188.       procedure HandleEvent(var Event: TEvent); virtual;
  189.       procedure HandleInput(FirstChar: String; Editing: Boolean); virtual;
  190.       procedure InitCurrPos; virtual;
  191.       procedure InsertColToHash(Block: TBlock;
  192.                   Columns, StartInsCol: Word; var Deleted: Boolean); virtual;
  193.       procedure InsertColumns; virtual;
  194.       procedure InsertRowToHash(Block: TBlock;
  195.                   Rows, StartInsRow: Word; var Deleted: Boolean); virtual;
  196.       procedure InsertRows; virtual;
  197.       constructor Load(var S: TStream);
  198.       procedure LoadDelimited(FileName: PathStr); virtual;
  199.       procedure LoadHashTables(var S: TStream; AdjustAfter: CellPos;
  200.                   RowAdjustment, ColAdjustment: Integer); virtual;
  201.       procedure LoadTablesFromTempFile(AdjustAfter: CellPos;
  202.                   RowAdjustment, ColAdjustment: Integer); virtual;
  203.       procedure LocateCursorWithMouse(Event: TEvent); virtual;
  204.       procedure MoveCell(OldPos: CellPos); virtual;
  205.       procedure MoveCellBlock; virtual;
  206.       procedure MoveDown; virtual;
  207.       procedure MoveHome; virtual;
  208.       procedure MoveLeft; virtual;
  209.       procedure MovePgDown; virtual;
  210.       procedure MovePgLeft; virtual;
  211.       procedure MovePgRight; virtual;
  212.       procedure MovePgUp; virtual;
  213.       procedure MoveRight; virtual;
  214.       procedure MoveUp; virtual;
  215.       function  OverwriteHashStart: BucketRange; virtual;
  216.       function  Parser: PParserObject; virtual;
  217.       procedure PasteBlock(DestBlock: TBlock; Formulas: Word); virtual;
  218.       procedure PasteCellBlock; virtual;
  219.       procedure Print; virtual;
  220.       procedure Recalc(Display: Boolean); virtual;
  221.       function  RowToY(Row: Integer): Byte; virtual;
  222.       function  SameCellPos(P1, P2 : CellPos) : Boolean; virtual;
  223.       procedure ScrollDraw; virtual;
  224.       function  SelectColumn(var Event: TEvent): Boolean; virtual;
  225.       procedure SetAreas(ScrollArea: TRect); virtual;
  226.       procedure SetBlankArea; virtual;
  227.       procedure SetChanged(IsChanged: Boolean); virtual;
  228.       procedure SetLimit(X, Y: Integer); virtual;
  229.       procedure SetLocked; virtual;
  230.       procedure SetNameWithMouse(var Event: TEvent); virtual;
  231.       procedure SetNumber(ANumber: Byte); virtual;
  232.       procedure SetProtection(Enable, Display: Boolean); virtual;
  233.       procedure SetScreenColStart(NewCol: Integer); virtual;
  234.       procedure SetScreenColStop(NewCol: Integer); virtual;
  235.       procedure SetScreenRowStart(NewRow: Integer); virtual;
  236.       procedure SetScreenRowStop(NewRow: Integer); virtual;
  237.       procedure SetState(AState: Word; Enable: Boolean); virtual;
  238.       procedure SetUnlocked; virtual;
  239.       procedure SortData; virtual;
  240.       function  SortObject : PSortObject; virtual;
  241.       procedure Store(var S: TStream);
  242.       procedure StoreHashTables(var S: TStream); virtual;
  243.       procedure StoreTablesToTempFile; virtual;
  244.       procedure ToggleAutoCalc; virtual;
  245.       procedure ToggleBlockOn; virtual;
  246.       procedure ToggleDisplayHeaders; virtual;
  247.       procedure ToggleEnd; virtual;
  248.       procedure ToggleFormulaDisplay; virtual;
  249.       function  TrackCursor: Boolean; virtual;
  250.       procedure UpdateScreenBlockDisplay; virtual;
  251.       function  WidthHashStart:BucketRange; virtual;
  252.       function  XToCol(X: Byte): Integer; virtual;
  253.       function  YToRow(Y: Byte): Integer; virtual;
  254.       procedure DoneHashTables; virtual;
  255.       destructor Done; virtual;
  256.    end; {...TSpreadSheet }
  257.  
  258.  
  259. procedure RegisterSpreadSheet;
  260. { Register all the units in OOGrid Library(TM) v1.0 }
  261.  
  262. procedure RegisterGLTSheet;
  263. { Register this unit's objects }
  264.  
  265. const
  266.    RSpreadSheet : TStreamRec = (
  267.       ObjType : stRSpreadSheet;
  268.       VmtLink : Ofs(TypeOf(TSpreadSheet)^);
  269.       Load    : @TSpreadSheet.Load;
  270.       Store   : @TSpreadSheet.Store
  271.    );
  272.  
  273. {****************************************************************************}
  274.                                implementation
  275. {****************************************************************************}
  276.  
  277. uses App, Memory, Dialogs, TCUtil, MsgBox, StdDlg, GLWindow;
  278.  
  279. const
  280.   OOGridFileHeader = 'OOGridLv1.00';
  281.   { All TSpreadSheet objects stored in a stream will be identified by
  282.     this file header }
  283.  
  284. {****************************************************************************}
  285. {**             Clipboard variables, procedures and functions              **}
  286. {****************************************************************************}
  287.  
  288. type
  289.   BlockOperation = (opCopy, opMove);
  290.   { Used by the clipboard record to indicate what kind of operation
  291.     was requested }
  292.  
  293.   ClipBoardRecord = RECORD
  294.   { This record is used to store information necessary for copy and move
  295.     operations }
  296.     Active            : Boolean;
  297.     SourceSpreadSheet : PSpreadSheet;
  298.     SourceCellHash    : PCellHashTable;
  299.     BlockToCopy       : PBlock;
  300.     CopyBlock         : Boolean;
  301.     Operation         : BlockOperation;
  302.   end; {...ClipBoardRecord }
  303.  
  304. var
  305.   Clipboard : ClipBoardRecord;
  306.  
  307. procedure InitClipBoard;
  308. { Resets the ClipBoard fields }
  309. begin
  310.   with ClipBoard do
  311.   begin
  312.     Active := False;
  313.     SourceSpreadSheet := NIL;
  314.     SourceCellHash := NIL;
  315.     if BlockToCopy <> NIL then
  316.     begin
  317.       Dispose(BlockToCopy);
  318.       BlockToCopy := NIL;
  319.     end; {...if BlockToCopy <> NIL }
  320.     Operation := opCopy;
  321.     CopyBlock := False;
  322.   end; {...with ClipBoard }
  323. end; {...InitClipBoard }
  324.  
  325. procedure ToggleClipBoardOn(SpreadSheet: PSpreadSheet; Block: PBlock;
  326.   ABlockOn: Boolean; Op: BlockOperation);
  327. { Sets the Clipboard fields for a copy or move operation }
  328. begin
  329.   with Clipboard do
  330.   begin
  331.     Active := True;
  332.     SourceSpreadSheet := SpreadSheet;
  333.     SourceCellHash := @SpreadSheet^.CellHash;
  334.     BlockToCopy := Block;
  335.     CopyBlock := ABlockOn;
  336.     Operation := Op;
  337.   end; {...with ClipBoard }
  338.   if Op = opCopy then
  339.     begin
  340.       if not DisplayMessage(GLStringList^.Get(sCopyCellsMsg)) then
  341.       begin
  342.         Application^.OutOfMemory;
  343.         InitClipBoard;
  344.       end; {...if not DisplayMessage(GLStringList^.Get(sCopyCellsMsg)) }
  345.     end {...if Op = opCopy }
  346.   else
  347.     begin
  348.       if not DisplayMessage(GLStringList^.Get(sMoveCellsMsg)) then
  349.       begin
  350.         Application^.OutOfMemory;
  351.         InitClipBoard;
  352.       end; {...if not DisplayMessage(GLStringList^.Get(sMoveCellsMsg)) }
  353.     end; {...if/else }
  354. end; {...ToggleClipBoardOn }
  355.  
  356.  
  357. procedure ToggleClipBoardOff;
  358. { Clears the ClipBoard }
  359. begin
  360.   InitClipBoard;
  361.   EraseMessage;
  362. end; {...ToggleClipBoardOff }
  363.  
  364.  
  365. {****************************************************************************}
  366. {**                         GetColWidth function                           **}
  367. {****************************************************************************}
  368.  
  369. function GetColWidth(var WHash : TWidthHashTable; C : Word) : Byte;
  370. { Gets the width of a column }
  371. var
  372.   W : Word;
  373. begin
  374.   W := WHash.Search(C);
  375.   if W = 0 then
  376.     GetColWidth := WHash.GetDefaultColWidth
  377.   else
  378.     GetColWidth := W;
  379. end; {...GetColWidth }
  380.  
  381. {****************************************************************************}
  382. {**                        Unit's Register procedures                      **}
  383. {****************************************************************************}
  384.  
  385. procedure RegisterSpreadSheet;
  386. { Register all streamable objects of the spreadsheet }
  387. begin
  388.    RegisterGLTSheet;
  389.    RegisterGLSupprt;
  390.    RegisterGLCell;
  391.    RegisterGLViews;
  392. end; {...RegisterSpreadSheet }
  393.  
  394. procedure RegisterGLTSheet;
  395. begin
  396.   RegisterType(RSpreadSheet);
  397. end; {...RegisterGLTSheet }
  398.  
  399. {****************************************************************************}
  400. {**                          TSpreadSheet Object                           **}
  401. {****************************************************************************}
  402.  
  403. constructor TSpreadSheet.Init(var Bounds: TRect;
  404.   InitCells: LongInt; AEmptyRowsAtTop, AEmptyRowsAtBottom: Byte;
  405.   AHScrollBar, AVScrollBar: PLimScrollBar; AInitMaxCols,
  406.   AInitMaxRows: Integer; InitDefaultColWidth, InitDefaultDecimalPlaces,
  407.   InitMaxDecimalPlaces: Byte; InitDefaultCurrency: CurrencyStr);
  408. const
  409.   MinRowsToDisplay = 2;
  410. var
  411.   CellPosition : CellPos;
  412.   R : TRect;
  413. begin
  414.   if not TScroller.Init(Bounds, AHScrollBar, AVScrollBar) then
  415.     Fail;
  416.   Delta.X := 1;
  417.   Delta.Y := 1;
  418.   EventMask := evMouseDown + evKeyDown + evCommand + evBroadCast;
  419.   Options := Options and not ofBuffered;
  420.   GrowMode := gfGrowHiX + gfGrowHiY;
  421.   HScrollBar^.EventMask := HScrollBar^.EventMask and not evKeyDown;
  422.   VScrollBar^.EventMask := VScrollBar^.EventMask and not evKeyDown;
  423.   EnableCommands([cmRecalc, cmToggleAutoCalc, cmToggleFormulas,
  424.     cmEditCell, cmGoToCell, cmChangeColWidth, cmDeleteColumns,
  425.     cmInsertColumns, cmDeleteRows, cmInsertRows, cmFormatCells,
  426.     cmFormatDefault, cmClear, cmCopy, cmPaste, cmCut, cmChangeColHeaders,
  427.     cmDeleteColHeaders, cmToggleHeaders, cmToggleProtection, cmSetUnlocked,
  428.     cmSetLocked, cmSortData, cmPrintSheet]);
  429.   if not CellHash.Init(CellHashStart(InitCells)) then
  430.     Fail;
  431.   if not WidthHash.Init(WidthHashStart, InitDefaultColWidth) then
  432.   begin
  433.     CellHash.Done;
  434.     Fail;
  435.   end; {...if not WidthHash.Init }
  436.   if not OverwriteHash.Init(OverwriteHashStart) then
  437.   begin
  438.     CellHash.Done;
  439.     WidthHash.Done;
  440.     Fail;
  441.   end; {...if not OverWriteHash.Init }
  442.   if not FormatHash.Init then
  443.   begin
  444.     CellHash.Done;
  445.     WidthHash.Done;
  446.     OverwriteHash.Done;
  447.     Fail;
  448.   end; {...if not FormatHash.Init }
  449.   if not ColHeadersHash.Init(ColHeadersHashStart) then
  450.   begin
  451.     CellHash.Done;
  452.     WidthHash.Done;
  453.     OverWriteHash.Done;
  454.     FormatHash.Done;
  455.     Fail;
  456.   end; {...if not ColHeadersHash.Init }
  457.   if not UnlockedHash.Init then
  458.   begin
  459.     CellHash.Done;
  460.     WidthHash.Done;
  461.     OverWriteHash.Done;
  462.     FormatHash.Done;
  463.     ColHeadersHash.Done;
  464.     Fail;
  465.   end; {...if not UnlockedHash.Init }
  466.   EmptyRowsAtTop := AEmptyRowsAtTop;
  467.   EmptyRowsAtBottom := AEmptyRowsAtBottom;
  468.   RowNumberSpace := 6;
  469.   MaxColWidth := Succ(ScreenCols - RowNumberSpace);
  470.   MaxScreenCols := MaxColWidth div DefaultMinColWidth;
  471.   GetMem(ColStart, MaxScreenCols);
  472.   if ColStart = NIL then
  473.   begin
  474.     CellHash.Done;
  475.     WidthHash.Done;
  476.     OverWriteHash.Done;
  477.     FormatHash.Done;
  478.     ColHeadersHash.Done;
  479.     UnlockedHash.Done;
  480.     Fail;
  481.   end; {...if ColStart = NIL }
  482.   InitCurrPos;
  483.   OldCurrPos := CurrPos;
  484.   LastPos := CurrPos;
  485.   BlockOn := False;
  486.   AutoCalc := False;
  487.   DisplayFormulas := False;
  488.   GoToEnd := False;
  489.   ScreenBlock := New(PBlock, Init(CurrPos));
  490.   CurrBlock := New(PBlock, Init(CurrPos));
  491.   DefaultColWidth := InitDefaultColWidth;
  492.   DefaultDecimalPlaces := InitDefaultDecimalPlaces;
  493.   DefaultCurrency := InitDefaultCurrency;
  494.   MaxDecimalPlaces := InitMaxDecimalPlaces;
  495.   MaxCols := AInitMaxCols;
  496.   MaxRows := AInitMaxRows;
  497.   GetExtent(R);
  498.   Inc(R.A.Y, EmptyRowsAtTop);
  499.   Dec(R.B.Y, EmptyRowsAtBottom);
  500.   SetAreas(R);
  501.   SetLimit(MaxCols, MaxRows);
  502.   DisplayHeaders := True;
  503.   SetProtection(False, False);
  504. end; {...TSpreadSheet.Init }
  505.  
  506.  
  507. function TSpreadSheet.AddCell(CellType: CellTypes; Pos: CellPos;
  508.   Error: Boolean; Value: Extended; Input: String): Boolean;
  509. { Adds a cell to the cell hash }
  510. var
  511.   OldLastPos : CellPos;
  512.   CellPtr, CP : PCell;
  513. begin
  514.   AddCell := False;
  515.   case CellType of
  516.     ClValue   : CellPtr := New(PValueCell, Init(Pos, Error, Value));
  517.     ClFormula : CellPtr := New(PFormulaCell, Init(Pos, Error, Value, Input));
  518.     ClText    : CellPtr := New(PTextCell, Init(Pos, Input));
  519.     ClRepeat  : CellPtr := New(PRepeatCell, Init(Pos, Input[2]));
  520.   end; {...case CellType }
  521.   if CellPtr = NIL then
  522.     Exit;
  523.   if not CellHash.Add(CellPtr) then
  524.   begin
  525.     Dispose(CellPtr, Done);
  526.     Exit;
  527.   end; {...if not CellHash.Add(CellPtr) }
  528.   OldLastPos := LastPos;
  529.   FindLastPos(Pos);
  530.   if not OverWriteHash.Add(CellPtr, CellHash, FormatHash, WidthHash, LastPos,
  531.     MaxCols, GetColWidth, DisplayFormulas, ChangeYes) then
  532.   begin
  533.     LastPos := OldLastPos;
  534.     CellHash.Delete(CellPtr^.Loc, CP);
  535.     Dispose(CellPtr, Done);
  536.     Exit;
  537.   end; {...if not OverWriteHash.Add }
  538.   AddCell := True;
  539. end; {...TSpreadSheet.AddCell }
  540.  
  541.  
  542. function TSpreadSheet.CellHashStart(TotalCells: LongInt): BucketRange;
  543. { Returns the initial number of buckets for the Cell hash table }
  544. begin
  545.   CellHashStart := Max(100, Min(MaxBuckets, TotalCells div 10));
  546. end; {...TSpreadSheet.CellHashStart}
  547.  
  548.  
  549. function TSpreadSheet.CellsProtected(Block: TBlock): Boolean;
  550. var
  551.   P : CellPos;
  552. begin
  553.   CellsProtected := False;
  554.   if SheetProtected then
  555.   begin
  556.     for P.Row := Block.Start.Row to Block.Stop.Row do
  557.       for P.Col := Block.Start.Col to Block.Stop.Col do
  558.         if not UnlockedHash.Search(P) then
  559.         begin
  560.           CellsProtected := True;
  561.           Exit;
  562.         end; {...if not UnlockedHash.Search(P) }
  563.   end; {...if SheetProtected }
  564. end; {...TSpreadSheet.CellsProtected }
  565.  
  566.  
  567. function TSpreadSheet.CellToFString(P: CellPos; var AColor: Byte): String;
  568. { Returns the formatted contents of a cell to be displayed in the screen }
  569. var
  570.   ColorFound, HasError : Boolean;
  571.   S1 : CurrencyStr;
  572.   F  : FormatType;
  573.   CP : PCell;
  574.   S  : String;
  575.   ClType : CellTypes;
  576. begin
  577.   AColor := FStringSituationColor(P, CP, HasError, ColorFound);
  578.   if HasError and not (DisplayFormulas and (CP^.CellType = ClFormula)) then
  579.     begin
  580.       S := GLStringList^.Get(sCellError);
  581.       S1 := '';
  582.       F := Ord(JCenter) shl JustShift;
  583.     end {...if HasError and ... }
  584.   else
  585.     begin
  586.       S := CP^.FormattedString(OverwriteHash, FormatHash, WidthHash,
  587.         GetColWidth, P, DisplayFormulas, 1, ColWidth(P.Col), S1, ClType);
  588.       if not ColorFound then
  589.         case ClType of
  590.           ClEmpty : AColor := GetColor(1);
  591.           ClText : AColor := GetColor(3);
  592.           ClValue : AColor := GetColor(2);
  593.           ClFormula : if DisplayFormulas then
  594.                         AColor := GetColor(5)
  595.                       else
  596.                         AColor := GetColor(2);
  597.           ClRepeat : AColor := GetColor(4);
  598.         end; {...case ClType }
  599.       F := CP^.Format(FormatHash, DisplayFormulas);
  600.     end; {...if/else }
  601.   if (Length(S1) + Length(S)) <= ColWidth(P.Col) then
  602.     case Justification((F shr JustShift) and JustPart) of
  603.       JLeft : CellToFString := S1 + LeftJustStr(S, ColWidth(P.Col) -
  604.                 Length(S1));
  605.       JCenter : CellToFString := S1 + CenterStr(S, ColWidth(P.Col) -
  606.                   Length(S1));
  607.       JRight : CellToFString := S1 + RightJustStr(S, ColWidth(P.Col) -
  608.                  Length(S1));
  609.     end {...case Justification((F shr JustShift) and JustPart) }
  610.   else
  611.     CellToFString := Copy(S1 + S, 1, ColWidth(P.Col));
  612. end; {...TSpreadSheet.CellToFString }
  613.  
  614.  
  615. procedure TSpreadSheet.ChangeBounds(var Bounds: TRect);
  616. { Changes the size of the spreadsheet and resets the limits of the scroller }
  617. begin
  618.   TScroller.ChangeBounds(Bounds);
  619.   SetLimit(MaxCols, MaxRows);
  620. end; {...TSpreadSheet.ChangeBounds }
  621.  
  622.  
  623. procedure TSpreadSheet.ChangeColHeaders;
  624. { Changes the header of a column or group of columns }
  625. var
  626.   Cancel, HeaderEntered : Boolean;
  627.   Dialog : PDialog;
  628.   CellPtr : PCell;
  629.   Column : Word;
  630.  
  631.     procedure GetValidHeader;
  632.     { Returns WidthEntered as true if a valid width was entered }
  633.     var
  634.       Code : Integer;
  635.     begin
  636.       if Desktop^.ExecView(Dialog) <> cmCancel then
  637.         begin
  638.           Dialog^.GetData(RChangeHeader);
  639.           HeaderEntered := True;
  640.         end {...if Desktop^.ExecView(Dialog) <> cmCancel }
  641.       else
  642.         Cancel := True;
  643.     end; {...GetValidHeader }
  644.  
  645. begin
  646.   Cancel := False;
  647.   HeaderEntered := False;
  648.   Dialog := PDialog(GLResFile^.Get('ChangeHeaderDialog'));
  649.   if not BlockOn or (BlockOn and (CurrBlock^.Start.Col = CurrBlock^.Stop.Col)) then
  650.     begin
  651.       if not ColHeadersHash.Search(CurrPos.Col, RChangeHeader.NewHeader) then
  652.         RChangeHeader.NewHeader := GLStringList^.Get(sColumnEntryIndicator) +
  653.         ' '+ColumnToString(CurrPos.Col)
  654.     end {...if not BlockOn or ... }
  655.   else
  656.     RChangeHeader.NewHeader := '';
  657.   Dialog^.SetData(RChangeHeader);
  658.   repeat
  659.     if (Application^.ValidView(Dialog) <> NIL) then
  660.       GetValidHeader
  661.     else
  662.       Exit;
  663.   until HeaderEntered or Cancel;
  664.   if not Cancel then
  665.   begin
  666.     with RChangeHeader do
  667.     begin
  668.       if Copy(NewHeader, 1, Length(GLStringList^.Get(sColumnEntryIndicator)))
  669.          = GLStringList^.Get(sColumnEntryIndicator) then
  670.         NewHeader := Copy(NewHeader, Length(GLStringList^.
  671.           Get(sColumnEntryIndicator))+2, Length(NewHeader) -
  672.           Length(GLStringList^.Get(sColumnEntryIndicator))+1);
  673.       with ColHeadersHash, CurrBlock^ do
  674.       begin
  675.         if BlockOn then
  676.           begin
  677.             for Column := Start.Col to Stop.Col do
  678.             begin
  679.               if NewHeader <> ColToString(Column) then
  680.               begin
  681.                 Delete(Column);
  682.                 if (NewHeader <> '') then
  683.                 begin
  684.                   if not Add(Column, NewHeader) then
  685.                     Exit;
  686.                 end; {...if NewHeader <> '' }
  687.               end; {...if NewHeader <> ColToString(Column) }
  688.               Delete(Column);
  689.               if (NewHeader <> '') and (NewHeader <> ColToString(Column)) then
  690.               begin
  691.                 if not Add(Column, NewHeader) then
  692.                   Exit;
  693.               end; {...if (NewHeader <> '') and ... }
  694.             end; {...for Column }
  695.           end {...if BlockOn }
  696.         else
  697.           begin
  698.             Delete(CurrPos.Col);
  699.             if (NewHeader <> '') or (NewHeader = ColToString(CurrPos.Col)) then
  700.               Add(CurrPos.Col, NewHeader);
  701.           end; {...if/else }
  702.       end; {...with ColHeadersHash, CurrBlock^ }
  703.     end; {...with RChangeHeader }
  704.     SetChanged(ModifiedYes);
  705.     DrawView;
  706.   end; {...if not Cancel }
  707.   Dispose(Dialog, Done);
  708. end; {...TSpreadSheet.ChangeColHeaders }
  709.  
  710.  
  711. procedure TSpreadSheet.ChangeColWidth;
  712. { Changes the width of a column or group of columns }
  713. var
  714.   Cancel, WidthEntered : Boolean;
  715.   NewWidth : Byte;
  716.   Dialog : PDialog;
  717.   CellPtr : PCell;
  718.   CurrWidth : String[10];
  719.   CellsOverWritten, Column : Word;
  720.  
  721.     procedure GetValidWidth(Dialog: PDialog; var Cancel,
  722.       WidthEntered: Boolean; var NewWidth: Byte);
  723.       { Returns WidthEntered as true if a valid width was entered }
  724.     var
  725.       Code : Integer;
  726.     begin
  727.       if Desktop^.ExecView(Dialog) <> cmCancel then
  728.         begin
  729.           Dialog^.GetData(RChangeWidth);
  730.           Val(RChangeWidth.NewWidth, NewWidth, Code);
  731.           if not ((NewWidth >= DefaultMinColWidth) and
  732.              (NewWidth <= MaxColWidth) or (NewWidth = 0)) then
  733.             MessageBox(GLStringList^.Get(sInvalidWidthMsg), NIL, mfError +
  734.               mfOKButton)
  735.           else
  736.             begin
  737.               WidthEntered := True;
  738.               if NewWidth = 0 then NewWidth := DefaultColWidth;
  739.             end; {...if/else }
  740.         end {...if Desktop^.ExecView(Dialog) <> cmCancel }
  741.       else
  742.         Cancel := True;
  743.    end; {...GetValidWidth }
  744.  
  745. begin
  746.   Cancel := False;
  747.   WidthEntered := False;
  748.   Dialog := PDialog(GLResFile^.Get('GetWidthDialog'));
  749.   if (not BlockOn) or (BlockOn and
  750.      (CurrBlock^.Start.Col = CurrBlock^.Stop.Col)) then
  751.     Str(ColWidth(CurrPos.Col), CurrWidth)
  752.   else
  753.     Str(DefaultColWidth, CurrWidth);
  754.   Dialog^.SetData(CurrWidth);
  755.   repeat
  756.     if (Application^.ValidView(Dialog) <> NIL) then
  757.       GetValidWidth(Dialog, Cancel, WidthEntered, NewWidth)
  758.     else
  759.       Exit;
  760.   until WidthEntered or Cancel;
  761.   if not Cancel then
  762.   begin
  763.     with WidthHash, CurrBlock^ do
  764.     begin
  765.       if BlockOn then
  766.         begin
  767.           for Column := Start.Col to Stop.Col do
  768.           begin
  769.             Delete(Column);
  770.             if NewWidth <> DefaultColWidth then
  771.             begin
  772.               if not Add(Column, NewWidth) then
  773.               Exit;
  774.             end; {...if NewWidth <> DefaultColWidth }
  775.           end; {...for Column }
  776.         end {...if BlockOn }
  777.       else
  778.         begin
  779.           Delete(CurrPos.Col);
  780.           if NewWidth <> DefaultColWidth then
  781.           begin
  782.             if not Add(CurrPos.Col, NewWidth) then
  783.               Exit;
  784.           end; {...if NewWidth <> DefaultColWidth }
  785.         end; {...if/else }
  786.     end; {...with WidthHash, CurrBlock^ }
  787.     SetScreenColStart(ScreenBlock^.Start.Col);
  788.     if CurrPos.Col > ScreenBlock^.Stop.Col then
  789.       HScrollBar^.SetValue(CurrPos.Col);
  790.     SetChanged(ModifiedYes);
  791.     with OverWriteHash do
  792.     begin
  793.       Done;
  794.       Init(OverWriteHashStart);
  795.     end; {with OverWriteHash }
  796.     FixOverWrite;
  797.     DrawView;
  798.   end; {...if not Cancel }
  799.   Dispose(Dialog, Done);
  800. end; {...TSpreadSheet.ChangeColWidth }
  801.  
  802.  
  803. procedure TSpreadSheet.CheckForDragging;
  804. var
  805.   ShiftState : Byte absolute $40:$17;
  806. begin
  807.   if (ShiftState and (kbRightShift + kbLeftShift)) <> 0 then
  808.     begin
  809.       if not BlockOn then
  810.         ToggleBlockOn;
  811.     end {...if ShiftState and (kbRightShift + kbLeftShift) }
  812.   else
  813.     ClearCurrBlock;
  814. end; {...TSpreadSheet.CheckForDragging }
  815.  
  816.  
  817. procedure TSpreadSheet.ClearCurrBlock;
  818. { Turns off the block mode and redisplays the affected cells }
  819. begin
  820.   if BlockOn then
  821.   begin
  822.     BlockOn := False;
  823.     DisplayBlock(CurrBlock^);
  824.   end; {...if BlockOn }
  825.   DisplayInfo;
  826. end; {...TSpreadSheet.ClearCurrBlock }
  827.  
  828.  
  829. procedure TSpreadSheet.ClearScreenArea(AreaToClear: PScreenArea);
  830. { Clears a given area of the screen }
  831. var
  832.   W, H : Byte;
  833.   B : TDrawBuffer;
  834. begin
  835.   with AreaToClear^ do
  836.   begin
  837.     W := Succ(LowerRight.Col - UpperLeft.Col);
  838.     H := Succ(LowerRight.Row - UpperLeft.Row);
  839.     MoveChar(B, ' ', Attrib, W);
  840.     WriteLine(UpperLeft.Col, UpperLeft.Row, W, H, B);
  841.   end; {...with AreaToClear^ }
  842. end; {...TSpreadSheet.ClearScreenArea }
  843.  
  844.  
  845. function TSpreadSheet.ColHeadersHashStart: BucketRange;
  846. { Returns the initial number of buckets for the Column Names hash table }
  847. begin
  848.   ColHeadersHashStart := 10;
  849. end; {...TSpreadSheet.ColHeadersHashStart }
  850.  
  851.  
  852. function TSpreadSheet.ColumnToString(Column: Word): String;
  853. { Converts a column to a string }
  854. var
  855.   HasName : Boolean;
  856.   S : String[4];
  857.   Name : String;
  858.   W : Word;
  859. begin
  860.   HasName := ColHeadersHash.Search(Column, Name);
  861.   if DisplayHeaders and HasName then
  862.     ColumnToString := Name
  863.   else
  864.     begin
  865.       if Column > 18278 then                     { Column is 4 letters }
  866.         S := Chr(Ord('A') + ((Column - 18279) div 17576))
  867.       else
  868.         S := '';
  869.       if Column > 702 then                { Column is at least 3 letters }
  870.         S := S + Chr(Ord('A') + (((Column - 703) mod 17576) div 676));
  871.       if Column > 26 then                 { Column is at least 2 letters }
  872.         S := S + Chr(Ord('A') + (((Column - 27) mod 676) div 26));
  873.       S := S + Chr(Ord('A') + (Pred(Column) mod 26));
  874.       ColumnToString := S;
  875.    end; {...if/else }
  876. end; {...TSpreadSheet.ColumnToString }
  877.  
  878.  
  879. function TSpreadsheet.ColToX(Col : Integer): Byte;
  880. { Returns the screen position of a given column }
  881. begin
  882.   ColToX := ColStart^[Col - ScreenBlock^.Start.Col];
  883. end; {...TSpreadSheet.ColToX }
  884.  
  885.  
  886. function TSpreadSheet.ColWidth(Col: Integer): Byte;
  887. { Returns the width of a certain column }
  888. var
  889.   Width : Integer;
  890. begin
  891.   Width := WidthHash.Search(Col);
  892.   if Width = 0 then
  893.     ColWidth := DefaultColWidth
  894.   else
  895.     ColWidth := Width;
  896. end; {...TSpreadSheet.ColWidth }
  897.  
  898.  
  899. procedure TSpreadSheet.CopyCellBlock;
  900. { Activates the clipboard and sets it to indicate the block to be copied }
  901. var
  902.   Block : PBlock;
  903. begin
  904.   if BlockOn then
  905.     begin
  906.       New(Block, Init(CurrBlock^.Start));
  907.       if Block = NIL then
  908.         Exit;
  909.       Block^.Stop := CurrBlock^.Stop;
  910.     end {...if BlockOn }
  911.   else
  912.     begin
  913.       New(Block, Init(CurrPos));
  914.       if Block = NIL then
  915.         Exit;
  916.       Block^.Stop := CurrPos;
  917.     end; {...if/else }
  918.   ToggleClipBoardOn(@Self, Block, BlockOn, opCopy);
  919. end; {...TSpreadSheet.CopyCellBlock }
  920.  
  921.  
  922. procedure TSpreadSheet.DeleteBlock(Block: TBlock; var Deleted: Boolean);
  923. { Deletes a block of cells }
  924. var
  925.   H, D : HashItemPtr;
  926.   CellPtr : PCell;
  927.   Counter : Word;
  928. begin
  929.   Deleted := False;
  930.   with CellHash, Block do
  931.   begin
  932.     for Counter := 1 to Buckets do
  933.     begin
  934.       H := HashData^[Counter];
  935.       while H <> NIL do
  936.       begin
  937.         D := H;
  938.         H := H^.Next;
  939.         Move(D^.Data, CellPtr, Sizeof(CellPtr));
  940.         with CellPtr^ do
  941.         begin
  942.           if CellInBlock(Loc) then
  943.             DeleteCell(Loc, Deleted);
  944.         end; {...with CellPtr^ }
  945.       end; {...while H <> NIL }
  946.     end; {...for Counter }
  947.   end; {...with CellHash, Block }
  948. end; {...TSpreadSheet.DeleteBlock }
  949.  
  950.  
  951. procedure TSpreadSheet.DeleteCell(Pos: CellPos; var Deleted: Boolean);
  952. { Deletes a single cell }
  953. var
  954.   CellPtr : PCell;
  955. begin
  956.   CellHash.Delete(Pos, CellPtr);
  957.   if CellPtr <> NIL then
  958.     begin
  959.       OverWriteHash.Delete(Pos, CellHash, FormatHash, WidthHash, LastPos,
  960.         MaxCols, GetColWidth, DisplayFormulas, ChangeYes);
  961.       Dispose(CellPtr, Done);
  962.       Deleted := True;
  963.     end {...if CellPtr <> NIL }
  964.   else
  965.     Deleted := False;
  966. end; {...TSpreadSheet.DeleteCell}
  967.  
  968.  
  969. procedure TSpreadSheet.DeleteColFromHash(Block: TBlock; Columns,
  970.   EndDelCol: Word; var Deleted: Boolean);
  971. { Deletes a column or block of columns from the hash tables }
  972. var
  973.   Start, Stop : CellPos;
  974.   H : HashItemPtr;
  975.   CellPtr : PCell;
  976.   Col : Word;
  977. const
  978.   CopyFormulasLiteral = $03;
  979. begin
  980.   DeleteBlock(Block, Deleted);
  981.   with CellHash do
  982.   begin
  983.     CellPtr := FirstItem;
  984.     while CellPtr <> NIL do
  985.     begin
  986.       with CellPtr^ do
  987.       begin
  988.         if CellPtr^.ShouldUpdate then
  989.            FixFormulaCol(CellPtr, opDelete, EndDelCol, Columns, MaxCols,
  990.              MaxRows);
  991.       end; {...with CellPtr^ }
  992.       CellPtr := NextItem;
  993.     end; {...while CellPtr <> NIL }
  994.   end; {...with CellHash }
  995.  
  996.   for Col := Block.Start.Col to Block.Stop.Col do
  997.     WidthHash.Delete(Col);
  998.   with WidthHash do
  999.   begin
  1000.     H := FirstItem;
  1001.     while H <> NIL do
  1002.     begin
  1003.       if WordPTr(@H^.Data)^ > EndDelCol then
  1004.         Dec(WordPtr(@H^.Data)^, Columns);
  1005.       H := NextItem;
  1006.     end; {...with H <> NIL }
  1007.   end; {...with WidthHash }
  1008.  
  1009.   Stop.Col := Block.Stop.Col;
  1010.   Stop.Row := MaxInt;
  1011.   FormatHash.Delete(Block.Start, Stop);
  1012.   with FormatHash do
  1013.   begin
  1014.     H := FirstItem;
  1015.     while H <> NIL do
  1016.     begin
  1017.       Move(H^.Data, Start, SizeOf(Start));
  1018.       Move(H^.Data[SizeOf(CellPos)], Stop, SizeOf(Stop));
  1019.       if (Start.Col > (EndDelCol - Columns)) and (Stop.Col <= EndDelCol) then
  1020.         Delete(Start, Stop)
  1021.       else
  1022.         begin
  1023.           if Start.Col > EndDelCol then
  1024.           begin
  1025.             Dec(Start.Col, Columns);
  1026.             Move(Start, H^.Data, Sizeof(Start));
  1027.           end; {...if Start.Col > EndDelCol }
  1028.           if Stop.Col > EndDelCol then
  1029.           begin
  1030.             Dec(Stop.Col, Columns);
  1031.             Move(Stop, H^.Data[Sizeof(CellPos)], Sizeof(Stop));
  1032.           end; {...if Stop.Col > EndDelCol }
  1033.         end; {...if/else }
  1034.       H := NextItem;
  1035.     end; {...while H <> NIL }
  1036.   end; {...with FormatHash }
  1037.  
  1038.   DeleteColHeaders(@Block);
  1039.   with ColHeadersHash do
  1040.   begin
  1041.     H := FirstItem;
  1042.     while H <> NIL do
  1043.     begin
  1044.       if WordPTr(@H^.Data)^ > EndDelCol then
  1045.         Dec(WordPtr(@H^.Data)^, Columns);
  1046.       H := NextItem;
  1047.     end; {...with H <> NIL }
  1048.   end; {...with ColHeadersHash }
  1049.  
  1050.   Stop.Col := Block.Stop.Col;
  1051.   Stop.Row := MaxInt;
  1052.   UnlockedHash.Delete(Block.Start, Stop);
  1053.   with UnlockedHash do
  1054.   begin
  1055.     H := FirstItem;
  1056.     while (H <> NIL) do
  1057.     begin
  1058.       Move(H^.Data, Start, SizeOf(Start));
  1059.       Move(H^.Data[SizeOf(CellPos)], Stop, SizeOf(Stop));
  1060.       if (Start.Col > (EndDelCol - Columns)) and (Stop.Col <= EndDelCol) then
  1061.         Delete(Start, Stop)
  1062.       else
  1063.         begin
  1064.           if Start.Col > EndDelCol then
  1065.           begin
  1066.             Dec(Start.Col, Columns);
  1067.             Move(Start, H^.Data, Sizeof(Start));
  1068.           end; {...if Start.Col > EndDelCol }
  1069.           if Stop.Col > EndDelCol then
  1070.           begin
  1071.             Dec(Stop.Col, Columns);
  1072.             Move(Stop, H^.Data[Sizeof(CellPos)], Sizeof(Stop));
  1073.           end; {...if Stop.Col > EndDelCol }
  1074.         end; {...if/else }
  1075.       H := NextItem;
  1076.     end; {...while H <> NIL }
  1077.   end; {...with UnlockedHash }
  1078. end; {...TSpreadSheet.DeleteColFromHash }
  1079.  
  1080.  
  1081. procedure TSpreadSheet.DeleteColHeaders(Block: PBlock);
  1082. { Deletes from the column headers hash table the headers of the selected
  1083.   columns }
  1084. var
  1085.   C : Word;
  1086. begin
  1087.   with Block^ do
  1088.   begin
  1089.     if Start.Col = Stop.Col then
  1090.       ColHeadersHash.Delete(Start.Col)
  1091.     else
  1092.       for C := Start.Col to Stop.Col do
  1093.         ColHeadersHash.Delete(C);
  1094.   end; {...with Block^ }
  1095. end; {...TSpreadSheet.DeleteColHeaders }
  1096.  
  1097.  
  1098. procedure TSpreadSheet.DeleteColumns;
  1099. { Deletes a column or group of columns }
  1100. var
  1101.   Dialog : PDialog;
  1102.   Deleted : Boolean;
  1103.   Pos, Start, Stop : CellPos;
  1104.   F : File;
  1105.   H : HashItemPtr;
  1106.   CellPtr : PCell;
  1107.   Block : TBlock;
  1108.   Columns, EndDelCol : Word;
  1109.   S : TBufStream;
  1110.   Items: LongInt;
  1111. begin
  1112.   Block.Start.Col := 0;
  1113.   Block.Start.Row := 0;
  1114.   Block.Stop.Col := 0;
  1115.   Block.Stop.Row := 0;
  1116.   Deleted := False;
  1117.   if BlockOn then
  1118.     begin
  1119.       if CurrBlock^.Start.Col <= LastPos.Col then
  1120.       begin
  1121.         with Block do
  1122.         begin
  1123.           Start.Col := CurrBlock^.Start.Col;
  1124.           Start.Row := 1;
  1125.           if CurrBlock^.Stop.Col > LastPos.Col then
  1126.             Stop.Col := LastPos.Col
  1127.           else
  1128.             Stop.Col := CurrBlock^.Stop.Col;
  1129.           Stop.Row := LastPos.Row;
  1130.         end; {...with Block }
  1131.       end; {...if CurrBlock^.Start.Col <= LastPos.Col }
  1132.       Columns := Succ(CurrBlock^.Stop.Col - CurrBlock^.Start.Col);
  1133.       EndDelCol := CurrBlock^.Stop.Col;
  1134.     end {...if BlockOn }
  1135.   else
  1136.     begin
  1137.       if CurrPos.Col <= LastPos.Col then
  1138.       begin
  1139.         with Block do
  1140.         begin
  1141.           Start.Col := CurrPos.Col;
  1142.           Start.Row := 1;
  1143.           Stop.Col := CurrPos.Col;
  1144.           Stop.Row := LastPos.Row;
  1145.         end; {...with Block }
  1146.       end; {...if CurrPos.Col <= LastPos.Col }
  1147.       Columns := 1;
  1148.       EndDelCol := CurrPos.Col;
  1149.     end; {...if/else }
  1150.   Dialog := PDialog(GLResFile^.Get('UpdatingTablesDialog'));
  1151.   if Application^.ValidView(Dialog) <> NIL then
  1152.     Desktop^.Insert(Dialog)
  1153.   else
  1154.     Exit;
  1155.   DeleteColFromHash(Block, Columns, EndDelCol, Deleted);
  1156.   StoreTablesToTempFile;
  1157.   DoneHashTables;
  1158.   Pos.Col := Succ(EndDelCol);
  1159.   Pos.Row := 0;
  1160.   LoadTablesFromTempFile(Pos, 0, -Columns);
  1161.   Assign(F, GLStringList^.Get(sTempFileName));
  1162.   Erase(F);
  1163.   if LastPos.Col > 1 then
  1164.     Dec(LastPos.Col, Columns);
  1165.   Pos.Col := EndDelCol - Columns;
  1166.   if Deleted then
  1167.     Pos.Row := LastPos.Row
  1168.   else
  1169.     Pos.Row := 1;
  1170.   FindLastPos(Pos);
  1171.   SetChanged(ModifiedYes);
  1172.   FixOverWrite;
  1173.   SetScreenColStart(ScreenBlock^.Start.Col);
  1174.   if AutoCalc then
  1175.     Recalc(DisplayNo);
  1176.   Desktop^.Delete(Dialog);
  1177.   Dispose(Dialog, Done);
  1178.   DrawView;
  1179. end; {...TSpreadSheet.DeleteColumns }
  1180.  
  1181.  
  1182. procedure TSpreadSheet.DeleteRowFromHash(Block: TBlock; Rows, EndDelRow: Word;
  1183.   var Deleted: Boolean);
  1184. { Deletes a row or block of rows from the hash tables }
  1185. var
  1186.   Start, Stop : CellPos;
  1187.   H : HashItemPtr;
  1188.   CellPtr : PCell;
  1189. begin
  1190.   DeleteBlock(Block, Deleted);
  1191.   with CellHash do
  1192.   begin
  1193.     CellPtr := FirstItem;
  1194.     while CellPtr <> NIL do
  1195.     begin
  1196.       with CellPtr^ do
  1197.       begin
  1198.         if CellPtr^.ShouldUpdate then
  1199.           FixFormulaRow(CellPtr, opDelete, EndDelRow, Rows, MaxCols, MaxRows);
  1200.       end; {...with CellPtr^ }
  1201.       CellPtr := NextItem;
  1202.     end; {...while CellPtr <> NIL }
  1203.   end; {...with CellHash }
  1204.  
  1205.   Stop.Col := MaxInt;
  1206.   Stop.Row := Block.Stop.Row;
  1207.   FormatHash.Delete(Block.Start, Stop);
  1208.   with FormatHash do
  1209.   begin
  1210.     H := FirstItem;
  1211.     while H <> NIL do
  1212.     begin
  1213.       Move(H^.Data, Start, SizeOf(Start));
  1214.       Move(H^.Data[SizeOf(CellPos)], Stop, SizeOf(Stop));
  1215.       if (Start.Row > (EndDelRow - Rows)) and (Stop.Row <= EndDelRow) then
  1216.         Delete(Start, Stop)
  1217.       else
  1218.         begin
  1219.           if Start.Row > EndDelRow then
  1220.           begin
  1221.             Dec(Start.Row, Rows);
  1222.             Move(Start, H^.Data, Sizeof(Start));
  1223.           end; {...if Start.Row > EndDelRow }
  1224.           if Stop.Row > EndDelRow then
  1225.           begin
  1226.             Dec(Stop.Row, Rows);
  1227.             Move(Stop, H^.Data[Sizeof(CellPos)], Sizeof(Stop));
  1228.           end; {...if Stop.Row > EndDelRow }
  1229.         end; {...if/else }
  1230.       H := NextItem;
  1231.     end; {...while H <> NIL }
  1232.   end; {...with FormatHash }
  1233.  
  1234.   Stop.Col := MaxInt;
  1235.   Stop.Row := Block.Stop.Row;
  1236.   UnlockedHash.Delete(Block.Start, Stop);
  1237.   with UnlockedHash do
  1238.   begin
  1239.     H := FirstItem;
  1240.     while H <> NIL do
  1241.     begin
  1242.       Move(H^.Data, Start, SizeOf(Start));
  1243.       Move(H^.Data[SizeOf(CellPos)], Stop, SizeOf(Stop));
  1244.       if (Start.Row > (EndDelRow - Rows)) and (Stop.Row <= EndDelRow) then
  1245.         Delete(Start, Stop)
  1246.       else
  1247.         begin
  1248.           if Start.Row > EndDelRow then
  1249.           begin
  1250.             Dec(Start.Row, Rows);
  1251.             Move(Start, H^.Data, Sizeof(Start));
  1252.           end; {...if Start.Row > EndDelRow }
  1253.           if Stop.Row > EndDelRow then
  1254.           begin
  1255.             Dec(Stop.Row, Rows);
  1256.             Move(Stop, H^.Data[Sizeof(CellPos)], Sizeof(Stop));
  1257.           end; {...if Stop.Row > EndDelRow }
  1258.         end; {...if/else }
  1259.       H := NextItem;
  1260.     end; {...while H <> NIL }
  1261.   end; {...with UnlockedHash }
  1262. end; {...TSpreadSheet.DeleteRowFromHash }
  1263.  
  1264.  
  1265. procedure TSpreadSheet.DeleteRows;
  1266. { Deletes a row or a group of rows }
  1267. var
  1268.   Dialog : PDialog;
  1269.   Deleted : Boolean;
  1270.   Pos, Start, Stop : CellPos;
  1271.   F : File;
  1272.   H : HashItemPtr;
  1273.   CellPtr : PCell;
  1274.   Block : TBlock;
  1275.   EndDelRow, Rows : Word;
  1276. begin
  1277.   Block.Start.Col := 0;
  1278.   Block.Start.Row := 0;
  1279.   Block.Stop.Col := 0;
  1280.   Block.Stop.Row := 0;
  1281.   Deleted := False;
  1282.   if BlockOn then
  1283.     begin
  1284.       if CurrBlock^.Start.Row <= LastPos.Row then
  1285.       begin
  1286.         with Block do
  1287.         begin
  1288.           Start.Col := 1;
  1289.           Start.Row := CurrBlock^.Start.Row;
  1290.           Stop.Col := LastPos.Col;
  1291.           if CurrBlock^.Stop.Row > LastPos.Row then
  1292.             Stop.Row := LastPos.Row
  1293.           else
  1294.             Stop.Row := CurrBlock^.Stop.Row;
  1295.         end; {...with Block }
  1296.       end; {...if CurrBlock^.Start.Row <= LastPos.Row }
  1297.       Rows := Succ(CurrBlock^.Stop.Row - CurrBlock^.Start.Row);
  1298.       EndDelRow := CurrBlock^.Stop.Row;
  1299.     end {...if BlockOn }
  1300.   else
  1301.     begin
  1302.       if CurrPos.Row <= LastPos.Row then
  1303.       begin
  1304.         with Block do
  1305.         begin
  1306.           Start.Col := 1;
  1307.           Start.Row := CurrPos.Row;
  1308.           Stop.Col := LastPos.Col;
  1309.           Stop.Row := CurrPos.Row;
  1310.         end; {...with Block }
  1311.       end; {if CurrPos.Row <= LastPos.Row }
  1312.       Rows := 1;
  1313.       EndDelRow := CurrPos.Row;
  1314.     end; {...if/else }
  1315.   Dialog := PDialog(GLResFile^.Get('UpdatingTablesDialog'));
  1316.   if Application^.ValidView(Dialog) <> NIL then
  1317.     Desktop^.Insert(Dialog)
  1318.   else
  1319.     Exit;
  1320.   DeleteRowFromHash(Block, Rows, EndDelRow, Deleted);
  1321.   StoreTablesToTempFile;
  1322.   DoneHashTables;
  1323.   Pos.Col := 0;
  1324.   Pos.Row := Succ(EndDelRow);
  1325.   LoadTablesFromTempFile(Pos, -Rows, 0);
  1326.   Assign(F, GLStringList^.Get(sTempFileName));
  1327.   Erase(F);
  1328.   if LastPos.Row > 1 then
  1329.     Dec(LastPos.Row, Rows);
  1330.   Pos.Row := EndDelRow - Rows;
  1331.   if Deleted then
  1332.     Pos.Col := LastPos.Col
  1333.   else
  1334.     Pos.Col := 1;
  1335.   FindLastPos(Pos);
  1336.   SetChanged(ModifiedYes);
  1337.   FixOverWrite;
  1338.   if AutoCalc then
  1339.     Recalc(DisplayNo);
  1340.   Desktop^.Delete(Dialog);
  1341.   Dispose(Dialog, Done);
  1342.   DrawView;
  1343. end; {...TSpreadSheet.DeleteRows }
  1344.  
  1345.  
  1346. procedure TSpreadSheet.DisplayAllCells;
  1347. { Displays all the cells in the current screen block }
  1348. begin
  1349.   ClearScreenArea(@DisplayArea);
  1350.   DisplayBlock(ScreenBlock^);
  1351. end; {...TSpreadSheet.DisplayAllCells }
  1352.  
  1353.  
  1354. procedure TSpreadSheet.DisplayBlankArea;
  1355. { Clears the empty area in the spreadsheet }
  1356. begin
  1357.   if not NoBlankArea then
  1358.     ClearScreenArea(@BlankArea);
  1359. end; {...TSpreadSheet.DisplayBlankArea }
  1360.  
  1361.  
  1362. procedure TSpreadSheet.DisplayBlock(B: TBlock);
  1363. { Displays a block of cells }
  1364. begin
  1365.   with B do
  1366.     DisplayCellBlock(Start.Col, Start.Row, Stop.Col, Stop.Row);
  1367. end; {...TSpreadSheet.DisplayBlock }
  1368.  
  1369.  
  1370. procedure TSpreadsheet.DisplayBlockDiff(B1, B2 : TBlock);
  1371. { Displays the cells present in one block, not present in the another block }
  1372. var
  1373.   Pass : Byte;
  1374.   B : TBlock;
  1375.   RefBlock, Block2, TempBlock : PBlock;
  1376. begin
  1377.   if Compare(B1, B2, SizeOf(TBlock)) then
  1378.     Exit;
  1379.   Pass := 0;
  1380.   RefBlock := @B1;
  1381.   Block2 := @B2;
  1382.   repeat
  1383.     Inc(Pass);
  1384.     if Block2^.Start.Col < RefBlock^.Start.Col then
  1385.     begin
  1386.       if Block2^.Start.Row < RefBlock^.Start.Row then
  1387.       begin
  1388.         B.Start := Block2^.Start;
  1389.         B.Stop.Col := Pred(RefBlock^.Start.Col);
  1390.         B.Stop.Row := Pred(RefBlock^.Start.Row);
  1391.         DisplayBlock(B);
  1392.       end; {...if Block2^.Start.Row < RefBlock^.Start.Row }
  1393.       if (Block2^.Start.Row >= RefBlock^.Start.Row) and
  1394.          (Block2^.Start.Row <= RefBlock^.Stop.Row) then
  1395.         begin
  1396.           B.Start.Col := Block2^.Start.Col;
  1397.           B.Start.Row := Block2^.Start.Row;
  1398.           B.Stop.Col := Pred(RefBlock^.Start.Col);
  1399.           B.Stop.Row := RefBlock^.Stop.Row;
  1400.           DisplayBlock(B);
  1401.         end {...if (Block2^.Start.Row >= RefBlock^.Start.Row) and ... }
  1402.       else if Block2^.Stop.Row <= RefBlock^.Stop.Row then
  1403.         begin
  1404.           B.Start.Col := Block2^.Start.Col;
  1405.           B.Start.Row := RefBlock^.Start.Row;
  1406.           B.Stop.Col := Pred(RefBlock^.Start.Col);
  1407.           B.Stop.Row := Min(RefBlock^.Stop.Row, Block2^.Stop.Row);
  1408.           DisplayBlock(B);
  1409.         end; {...else if Block2^.Stop.Row <= RefBlock^.Stop.Row }
  1410.       if Block2^.Stop.Row > RefBlock^.Stop.Row then
  1411.       begin
  1412.         B.Start.Col := Block2^.Start.Col;
  1413.         B.Start.Row := Succ(RefBlock^.Stop.Row);
  1414.         B.Stop.Col := Pred(RefBlock^.Start.Col);
  1415.         B.Stop.Row := Block2^.Stop.Row;
  1416.         DisplayBlock(B);
  1417.       end; {...if Block2^.Stop.Row > RefBlock^.Stop.Row }
  1418.     end; {...if Block2^.Start.Col < RefBlock^.Start.Col }
  1419.  
  1420.     if Block2^.Start.Row < RefBlock^.Start.Row then
  1421.     begin
  1422.       if (Block2^.Start.Col >= RefBlock^.Start.Col) and
  1423.          (Block2^.Start.Col <= RefBlock^.Stop.Col) then
  1424.         begin
  1425.           B.Start.Col := Block2^.Start.Col;
  1426.           B.Start.Row := Block2^.Start.Row;
  1427.           B.Stop.Col := RefBlock^.Stop.Col;
  1428.           B.Stop.Row := Pred(RefBlock^.Start.Row);
  1429.           DisplayBlock(B);
  1430.         end {...if (Block2^.Start.Col >= RefBlock^.Start.Col) and ... }
  1431.       else if Block2^.Stop.Col <= RefBlock^.Stop.Col then
  1432.         begin
  1433.           B.Start.Col := RefBlock^.Start.Col;
  1434.           B.Start.Row := Block2^.Start.Row;
  1435.           B.Stop.Col := Min(RefBlock^.Stop.Col, Block2^.Stop.Col);
  1436.           B.Stop.Row := Pred(RefBlock^.Start.Row);
  1437.           DisplayBlock(B);
  1438.         end; {...else if Block2^.Stop.Col <= RefBlock^.Stop.Col }
  1439.     end; {...if Block2^.Start.Row < RefBlock^.Start.Row }
  1440.  
  1441.     if Block2^.Stop.Row > RefBlock^.Stop.Row then
  1442.     begin
  1443.       if (Block2^.Start.Col >= RefBlock^.Start.Col) and
  1444.          (Block2^.Start.Col <= RefBlock^.Stop.Col) then
  1445.         begin
  1446.           B.Start.Col := Block2^.Start.Col;
  1447.           B.Start.Row := Succ(RefBlock^.Stop.Row);
  1448.           B.Stop.Col := RefBlock^.Stop.Col;
  1449.           B.Stop.Row := Block2^.Stop.Row;
  1450.           DisplayBlock(B);
  1451.         end {...if (Block2^.Start.Col >= RefBlock^.Start.Col) and ... }
  1452.       else if Block2^.Stop.Col <= RefBlock^.Stop.Col then
  1453.         begin
  1454.           B.Start.Col := RefBlock^.Start.Col;
  1455.           B.Start.Row := Succ(RefBlock^.Stop.Row);
  1456.           B.Stop.Col := Min(RefBlock^.Stop.Row, Block2^.Stop.Row);
  1457.           B.Stop.Row := Block2^.Stop.Row;
  1458.           DisplayBlock(B);
  1459.         end; {...else if Block2^.Stop.Col <= RefBlock^.Stop.Col }
  1460.     end; {...if Block2^.Stop.Row > RefBlock^.Stop.Row }
  1461.  
  1462.     if Block2^.Stop.Col > RefBlock^.Stop.Col then
  1463.     begin
  1464.       if Block2^.Start.Row < RefBlock^.Start.Row then
  1465.       begin
  1466.         B.Start.Col := Succ(RefBlock^.Stop.Col);
  1467.         B.Start.Row := Block2^.Start.Row;
  1468.         B.Stop.Col := Block2^.Stop.Col;
  1469.         B.Stop.Row := Pred(RefBlock^.Start.Row);
  1470.         DisplayBlock(B);
  1471.       end; {...if Block2^.Start.Row < RefBlock^.Start.Row }
  1472.       if (Block2^.Start.Row >= RefBlock^.Start.Row) and
  1473.          (Block2^.Start.Row <= RefBlock^.Stop.Row) then
  1474.         begin
  1475.           B.Start.Col := Succ(RefBlock^.Stop.Col);
  1476.           B.Start.Row := Block2^.Start.Row;
  1477.           B.Stop.Col := Block2^.Stop.Col;
  1478.           B.Stop.Row := RefBlock^.Stop.Row;
  1479.           DisplayBlock(B);
  1480.         end {...if (Block2^.Start.Row >= RefBlock^.Start.Row) and ... }
  1481.       else if Block2^.Stop.Row <= RefBlock^.Stop.Row then
  1482.         begin
  1483.           B.Start.Col := Succ(RefBlock^.Stop.Col);
  1484.           B.Start.Row := RefBlock^.Start.Row;
  1485.           B.Stop.Col := Block2^.Stop.Col;
  1486.           B.Stop.Row := Min(RefBlock^.Stop.Row, Block2^.Stop.Row);
  1487.           DisplayBlock(B);
  1488.         end; {...else if Block2^.Stop.Row <= RefBlock^.Stop.Row }
  1489.       if Block2^.Stop.Row > RefBlock^.Stop.Row then
  1490.       begin
  1491.         B.Start.Col := Succ(RefBlock^.Stop.Col);
  1492.         B.Start.Row := Succ(RefBlock^.Stop.Row);
  1493.         B.Stop := Block2^.Stop;
  1494.         DisplayBlock(B);
  1495.       end; {...if Block2^.Stop.Row > RefBlock^.Stop.Row }
  1496.     end; {...if Block2^.Stop.Col > RefBlock^.Stop.Col }
  1497.  
  1498.     TempBlock := RefBlock;
  1499.     RefBlock := Block2;
  1500.     Block2 := TempBlock;
  1501.   until (Pass = 2);
  1502. end; {...TSpreadSheet.DisplayBlockDiff }
  1503.  
  1504.  
  1505. procedure TSpreadsheet.DisplayCell(P : CellPos);
  1506. { Displays a single cell }
  1507. var
  1508.   Color : Byte;
  1509.   S     : String[ScreenCols];
  1510.   B     : TDrawBuffer;
  1511. begin
  1512.   S := CellToFString(P, Color);
  1513.   MoveStr(B, S, Color);
  1514.   WriteLine(ColToX(P.Col), RowToY(P.Row), Length(S), 1, B);
  1515. end; {...TSpreadSheet.DisplayCell }
  1516.  
  1517.  
  1518. procedure TSpreadSheet.DisplayCellBlock(C1, R1, C2, R2: Word);
  1519. { Displays a block of cells }
  1520. var
  1521.   P : CellPos;
  1522. begin
  1523.   with ScreenBlock^ do
  1524.   begin
  1525.     for P.Row := Max(R1, Start.Row) to Min(R2, Stop.Row) do
  1526.       for P.Col := Max(C1, Start.Col) to Min(C2, Stop.Col) do
  1527.          DisplayCell(P);
  1528.   end; {...with ScreenBlock^ }
  1529. end; {...TSpreadSheet.DisplayCellBlock }
  1530.  
  1531.  
  1532. procedure TSpreadSheet.DisplayCellData;
  1533. var
  1534.   InfoStringLength, W  : Byte;
  1535.   CP : PCell;
  1536.   CurrWidth, LockedState, S  : String;
  1537.   B  : TDrawBuffer;
  1538.   Pos : CellPos;
  1539. const
  1540.   BlockInfoSize = 30;
  1541.   CellInfoSize = 28;
  1542. begin
  1543.   if (State and sfActive <> 0) then
  1544.     Pos := CurrPos
  1545.   else
  1546.     Pos := OldCurrPos;
  1547.   CP := CellHash.Search(Pos);
  1548.   ClearScreenArea(@DataArea);
  1549.   Str(ColWidth(Pos.Col), CurrWidth);
  1550.   LockedState := '';
  1551.   if UnlockedHash.Search(Pos) then
  1552.     LockedState := GLStringList^.Get(sCellUnLockedInfo)
  1553.   else
  1554.     if SheetProtected then
  1555.       LockedState := GLStringList^.Get(sCellLockedInfo);
  1556.   with DataArea do
  1557.   begin
  1558.     S := LeftJustStr(ColToString(Pos.Col) + RowToString(Pos.Row) +
  1559.       ' [' + GLStringList^.Get(sWidthLetter) + CurrWidth + '] ' + CP^.Name +
  1560.       ' ' + LockedState, CellInfoSize);
  1561.     InfoStringLength := CellInfoSize;
  1562.     if BlockOn then
  1563.     begin
  1564.       with CurrBlock^ do
  1565.       begin
  1566.         S := S + LeftJustStr(GLStringList^.Get(sBlockName) +
  1567.           ColToString(Start.Col) + RowToString(Start.Row) + '..' +
  1568.           ColToString(Stop.Col) + RowToString(Stop.Row), BlockInfoSize);
  1569.         InfoStringLength := InfoStringLength + BlockInfoSize
  1570.       end; {...with CurrBlock^ }
  1571.     end; {...if BlockOn }
  1572.     MoveStr(B, S, GetColor(8));
  1573.     WriteLine(UpperLeft.Col, UpperLeft.Row, InfoStringLength, 1, B);
  1574.   end; {...with DataArea }
  1575.   with ContentsArea do
  1576.   begin
  1577.     S := LeftJustStr(CP^.DisplayString(DisplayFormulas, MaxDecimalPlaces),
  1578.       Succ(LowerRight.Col-UpperLeft.Col));
  1579.     MoveStr(B, S, GetColor(9));
  1580.     WriteLine(UpperLeft.Col, UpperLeft.Row, Length(S), 1, B);
  1581.   end; {...with ContenstArea }
  1582. end; {...TSpreadSheet.DisplayCellData }
  1583.  
  1584.  
  1585. procedure TSpreadSheet.DisplayCols;
  1586. { Displays the column headers }
  1587. var
  1588.   W, X : Byte;
  1589.   C : Integer;
  1590.   B : TDrawBuffer;
  1591. begin
  1592.   with ScreenBlock^ do
  1593.   begin
  1594.     for C := Start.Col to Stop.Col do
  1595.     begin
  1596.       W := ColWidth(C);
  1597.       MoveStr(B, CenterStr(ColumnToString(C), W), ColArea.Attrib);
  1598.       WriteLine (ColStart^[C - Start.Col], ColArea.UpperLeft.Row, W, 1, B);
  1599.     end; {...for C }
  1600.     if not NoBlankArea then
  1601.     begin
  1602.       X := ColStart^[Stop.Col - Start.Col]+ColWidth(Stop.Col);
  1603.       W := Size.X - X;
  1604.       MoveChar(B, ' ', ColArea.Attrib, W);
  1605.       WriteLine(X, ColArea.UpperLeft.Row, W, 1, B);
  1606.     end; {...if not NoBlankArea }
  1607.   end; {...with ScreenBlock^ }
  1608. end; {...TSpreadSheet.DisplayCols }
  1609.  
  1610.  
  1611. procedure TSpreadSheet.DisplayInfo;
  1612. { Displays the spreadsheet's info characters }
  1613. var
  1614.   Width     : Byte;
  1615.   Info      : String;
  1616.   B         : TDrawBuffer;
  1617. begin
  1618.   ClearScreenArea(@InfoArea);
  1619.   with InfoArea do
  1620.   begin
  1621.     Width := Succ(LowerRight.Col - UpperLeft.Col);
  1622.     Info := ColToString(Number);
  1623.     if Length(Info) = 1 then
  1624.       Info := Info + ' ';
  1625.     if GoToEnd then
  1626.       Info := Info + GLStringList^.Get(sEndKeyPressedLetter)
  1627.     else
  1628.       Info := Info + ' ';
  1629.     if DisplayHeaders then
  1630.       Info := Info + GLStringList^.Get(sDisplayHeadersLetter)
  1631.     else
  1632.       Info := Info + ' ';
  1633.     if AutoCalc then
  1634.       Info := Info + GLStringList^.Get(sAutoCalcLetter)
  1635.     else
  1636.       Info := Info + ' ';
  1637.     if DisplayFormulas then
  1638.       Info := Info + GLStringList^.Get(sDisplayFormulasLetter)
  1639.     else
  1640.       Info := Info + ' ';
  1641.     MoveStr(B, Info, Attrib);
  1642.     Writeline (UpperLeft.Col, UpperLeft.Row, Min(Width, Length(Info)), 1, B);
  1643.   end; {...with InfoArea }
  1644. end; {...TSpreadSheet.DisplayInfo }
  1645.  
  1646.  
  1647. procedure TSpreadSheet.DisplayRows;
  1648. { Displays row numbers }
  1649. var
  1650.   R : Integer;
  1651.   B : TDrawBuffer;
  1652. begin
  1653.   with ScreenBlock^ do
  1654.   begin
  1655.     for R := Start.Row to Stop.Row do
  1656.       with RowArea do
  1657.       begin
  1658.         MoveStr(B, LeftJustStr(RowToString(R), RowNumberSpace),
  1659.           RowArea.Attrib);
  1660.         WriteLine(UpperLeft.Col, R - Start.Row + UpperLeft.Row,
  1661.           RowNumberSpace, 1, B);
  1662.       end; {...with RowArea }
  1663.   end; {...with ScreenBlock^ }
  1664. end; {...TSpreadSheet.DisplayRows }
  1665.  
  1666.  
  1667. procedure TSpreadSheet.DoAfterEndInput;
  1668. { This procedure is called after a cell is added or modified }
  1669. begin
  1670.   MoveDown;
  1671. end; {...TSpreadSheet.DoAfterEndInput}
  1672.  
  1673. procedure TSpreadSheet.DragCursorWithMouse(Event: TEvent);
  1674. { Sets block mode on and extends the block to wherever the mouse is pointing }
  1675. var
  1676.    ColScrPos : Byte;
  1677.    OldPos : CellPos;
  1678.    Counter : Integer;
  1679.    Mouse : TPoint;
  1680. begin
  1681.   MakeLocal(Event.Where, Mouse);
  1682.   with ScreenBlock^ do
  1683.   begin
  1684.     KeyPressed := True;
  1685.     if not BlockOn then ToggleBlockOn;
  1686.     OldPos := CurrPos;
  1687.     if Mouse.Y < DisplayArea.UpperLeft.Row then
  1688.       begin
  1689.         CurrPos.Row := Max(1, Pred(Start.Row));
  1690.         SetScreenRowStart(CurrPos.Row);
  1691.         VScrollBar^.SetValue(ScreenBlock^.Start.Row);
  1692.       end {...if Mouse.Y < DisplayArea.UpperLeft.Row }
  1693.     else if Mouse.Y > DisplayArea.LowerRight.Row then
  1694.       begin
  1695.         CurrPos.Row := Min(MaxRows, Succ(Stop.Row));
  1696.         SetScreenRowStop(CurrPos.Row);
  1697.         VScrollBar^.SetValue(ScreenBlock^.Start.Row);
  1698.       end {...if Mouse.Y > DisplayArea.LowerRight.Row }
  1699.     else
  1700.       CurrPos.Row := YToRow(Mouse.Y);
  1701.     if (Mouse.X >= Size.X) or (not NoBlankArea and
  1702.        (Mouse.X >= BlankArea.UpperLeft.Col)) then
  1703.       begin
  1704.         CurrPos.Col := Min(MaxCols, Succ(Stop.Col));
  1705.         SetScreenColStop(CurrPos.Col);
  1706.         HScrollBar^.SetValue(ScreenBlock^.Start.Col);
  1707.       end {...if (Mouse.X >= Size.X) or... }
  1708.     else if Mouse.X < RowNumberSpace then
  1709.       begin
  1710.         CurrPos.Col := Max(1, Pred(Start.Col));
  1711.         SetScreenColStart(CurrPos.Col);
  1712.         HScrollBar^.SetValue(ScreenBlock^.Start.Col);
  1713.       end {...else if Mouse.X < RowNumberSpace }
  1714.     else
  1715.       CurrPos.Col := XToCol(Mouse.X);
  1716.     MoveCell(OldPos);
  1717.     KeyPressed := False;
  1718.   end; {...with ScreenBlock^ }
  1719. end; {...TSpreadSheet.DragCursorWithMouse }
  1720.  
  1721.  
  1722. procedure TSpreadSheet.Draw;
  1723. { Sets the spreadsheet areas and displays all the spreadsheet's components }
  1724. var
  1725.   R : TRect;
  1726. begin
  1727.   GetExtent(R);
  1728.   Inc(R.A.Y, EmptyRowsAtTop);
  1729.   Dec(R.B.Y, EmptyRowsAtBottom);
  1730.   SetAreas(R);
  1731.   DisplayCols;
  1732.   DisplayRows;
  1733.   DisplayInfo;
  1734.   DisplayAllCells;
  1735.   DisplayCellData;
  1736.   DisplayBlankArea;
  1737. end; {...TSpreadSheet.Draw }
  1738.  
  1739.  
  1740. procedure TSpreadSheet.EraseCellBlock(EraseBlock: Boolean);
  1741. { Deletes a cell or block of cells }
  1742. var
  1743.   Deleted: Boolean;
  1744.   Pos : CellPos;
  1745. begin
  1746.   Deleted := False;
  1747.   if not BlockOn or not EraseBlock then
  1748.     begin
  1749.       if not SheetProtected or (SheetProtected and
  1750.          UnlockedHash.Search(CurrPos)) then
  1751.         begin
  1752.           DeleteCell(CurrPos, Deleted);
  1753.           Pos := CurrPos;
  1754.         end {...if not SheetProtected or ... }
  1755.       else
  1756.         MessageBox(GLStringList^.Get(sCellsProtectedMsg), NIL, mfInformation +
  1757.           mfOKButton);
  1758.     end {...if not BlockOn or not EraseBlock }
  1759.   else
  1760.     begin
  1761.       if not CellsProtected(CurrBlock^) then
  1762.         begin
  1763.           DisplayMessage(GLStringList^.Get(sBlockDeleteMsg));
  1764.           DeleteBlock(CurrBlock^, Deleted);
  1765.           EraseMessage;
  1766.           Pos := CurrBlock^.Stop;
  1767.           if Deleted then
  1768.             ClearCurrBlock;
  1769.         end {...if not CellsProtected(CurrBlock^) }
  1770.       else
  1771.         MessageBox(GLStringList^.Get(sCellsProtectedMsg), NIL, mfInformation +
  1772.           mfOKButton);
  1773.     end; {...if/else }
  1774.   if Deleted then
  1775.   begin
  1776.     Desktop^.Lock;
  1777.     FindLastPos(Pos);
  1778.     SetChanged(ModifiedYes);
  1779.     if AutoCalc then
  1780.       Recalc(DisplayYes);
  1781.     DisplayAllCells;
  1782.     DisplayCellData;
  1783.     Desktop^.Unlock;
  1784.   end; {...if Deleted }
  1785. end; {...TSpreadSheet.EraseCellBlock }
  1786.  
  1787.  
  1788. procedure TSpreadSheet.ExtendCurrBlock(Redraw : Boolean);
  1789. { Resizes the current block if active }
  1790. var
  1791.   OldBlock : TBlock;
  1792. begin
  1793.   if BlockOn then
  1794.   begin
  1795.     Move(CurrBlock^, OldBlock, SizeOf(CurrBlock^));
  1796.     if CurrBlock^.ExtendTo(CurrPos) then
  1797.       begin
  1798.         if Redraw then
  1799.           DisplayBlockDiff(OldBlock, CurrBlock^);
  1800.       end {...if CurrBlock^.ExtendTo(CurrPos) }
  1801.     else
  1802.       ClearCurrBlock;
  1803.   end; {...if BlockOn }
  1804. end; {...TSpreadSheet.ExtendCurrBlock }
  1805.  
  1806.  
  1807. procedure TSpreadsheet.FindLastPos(DPos : CellPos);
  1808. { Finds the lower left corner of smallest block containing used cells }
  1809. var
  1810.   ColFound, RowFound : Boolean;
  1811.   CellPtr : PCell;
  1812.   Counter : Word;
  1813. begin
  1814.   with CellHash do
  1815.   begin
  1816.     ColFound := DPos.Col < LastPos.Col;
  1817.     RowFound := DPos.Row < LastPos.Row;
  1818.     if (not ColFound) or (not RowFound) then
  1819.     begin
  1820.       if not ColFound then
  1821.         LastPos.Col := 1;
  1822.       if not RowFound then
  1823.         LastPos.Row := 1;
  1824.       CellPtr := FirstItem;
  1825.       while CellPtr <> NIL do
  1826.       begin
  1827.         if not ColFound then
  1828.         begin
  1829.           if CellPtr^.Loc.Col > LastPos.Col then
  1830.           begin
  1831.             LastPos.Col := CellPtr^.Loc.Col;
  1832.             PLimScrollBar(HScrollBar)^.DisplayLimit := Max(DefaultHScrollBarLimit, LastPos.Col);
  1833.             ColFound := LastPos.Col = DPos.Col;
  1834.             if ColFound and RowFound then
  1835.               Exit;
  1836.           end; {...if CellPtr^.Loc.Col > LastPos.Col }
  1837.         end; {...if not ColFound }
  1838.         if not RowFound then
  1839.         begin
  1840.           if CellPtr^.Loc.Row > LastPos.Row then
  1841.           begin
  1842.             LastPos.Row := CellPtr^.Loc.Row;
  1843.             PLimScrollBar(VScrollBar)^.DisplayLimit := Max(DefaultVScrollBarLimit, LastPos.Row);
  1844.             RowFound := LastPos.Row = DPos.Row;
  1845.             if ColFound and RowFound then
  1846.               Exit;
  1847.           end; {...if CellPtr^.Loc.Row > LastPos.Row }
  1848.         end; {...if not RowFound }
  1849.         CellPtr := NextItem;
  1850.       end; {...while CellPtr <> NIL }
  1851.     end; {...if (not ColFound) or (not RowFound) }
  1852.   end; {...with CellHash }
  1853. end; {...TSpreadSheet.FindLastPos }
  1854.  
  1855.  
  1856.  
  1857. procedure TSpreadSheet.FindScreenColStart;
  1858. { Find the starting screen column when the ending column is known}
  1859. var
  1860.   Temp, Width : Byte;
  1861.   Index, Place : Integer;
  1862. begin
  1863.   with ScreenBlock^ do
  1864.   begin
  1865.     Index := 0;
  1866.     Place := Succ(DisplayArea.LowerRight.Col);
  1867.     Width := ColWidth(Stop.Col);
  1868.     repeat
  1869.       ColStart^[Index] := Max(DisplayArea.UpperLeft.Col, Place - Width);
  1870.       Dec(Place, Width);
  1871.       Inc(Index);
  1872.       if (Stop.Col - Index = 0) then
  1873.         Width := 0
  1874.       else
  1875.         Width := ColWidth(Stop.Col - Index);
  1876.     until (Width = 0) or (Place - Width < DisplayArea.UpperLeft.Col);
  1877.     Start.Col := Succ(Stop.Col - Index);
  1878.     Dec(Index);
  1879.     if ColStart^[Index] > DisplayArea.UpperLeft.Col then
  1880.     begin
  1881.       Temp := ColStart^[Index] - DisplayArea.UpperLeft.Col;
  1882.       for Place := 0 to Index do
  1883.         Dec(ColStart^[Place], Temp);
  1884.     end; {...if ColStart^[Index] > DisplayArea.UpperLeft.Col }
  1885.     if Index > 0 then
  1886.     begin
  1887.       for Place := 0 to (Pred(Index) shr 1) do
  1888.         begin
  1889.           Temp := ColStart^[Index - Place];
  1890.           ColStart^[Index - Place] := ColStart^[Place];
  1891.           ColStart^[Place] := Temp;
  1892.         end; {...for Place }
  1893.     end; {...if Index > 0 }
  1894.   end; {...with ScreenBlock^ }
  1895. end; {...TSpreadSheet.FindScreenColStart }
  1896.  
  1897.  
  1898.  
  1899. procedure TSpreadSheet.FindScreenColStop;
  1900. { Finds then ending screen column when the starting column is known }
  1901. var
  1902.   Index, Place, Width : Byte;
  1903. begin
  1904.   with ScreenBlock^ do
  1905.   begin
  1906.     Index := 0;
  1907.     Place := DisplayArea.UpperLeft.Col;
  1908.     Width := ColWidth(Start.Col);
  1909.     repeat
  1910.       ColStart^[Index] := Place;
  1911.       Inc(Place, Width);
  1912.       Inc(Index);
  1913.       if (Integer(Index) + Start.Col > MaxCols) then
  1914.         Width := 0
  1915.       else
  1916.         Width := ColWidth(Index + Start.Col);
  1917.     until (Width = 0) or
  1918.     (Place + Width > Succ(DisplayArea.LowerRight.Col));
  1919.     Stop.Col := Pred(Start.Col + Index);
  1920.   end; {...with ScreenBlock^ }
  1921. end; {...TSpreadSheet.FindScreenColStop }
  1922.  
  1923.  
  1924. procedure TSpreadSheet.FindScreenRowStart;
  1925. { Finds the starting screen row when the ending row is know }
  1926. begin
  1927.   with ScreenBlock^ do
  1928.   begin
  1929.     if LongInt(Stop.Row) - TotalRows < 0 then
  1930.       begin
  1931.         Start.Row := 1;
  1932.         FindScreenRowStop;
  1933.       end {if LongInt(Stop.Row) - TotalRows < 0 }
  1934.     else
  1935.       Start.Row := Succ(Stop.Row - TotalRows);
  1936.   end; {...with ScreenBlock^ }
  1937. end; {...TSpreadSheet.FindScreenRowStart }
  1938.  
  1939.  
  1940. procedure TSpreadSheet.FindScreenRowStop;
  1941. { Finds the ending screen row when the starting row is know }
  1942. begin
  1943.   with ScreenBlock^ do
  1944.   begin
  1945.     if LongInt(Start.Row) + TotalRows > Succ(LongInt(MaxRows)) then
  1946.       begin
  1947.         Stop.Row := MaxRows;
  1948.         FindScreenRowStart;
  1949.       end {if (LongInt(Start.Row) + TotalRows) > Succ(MaxRows) }
  1950.     else
  1951.       Stop.Row := Pred(Start.Row + TotalRows);
  1952.   end; {...with ScreenBlock^ }
  1953. end; {...TSpreadSheet.FindScreenRowStop }
  1954.  
  1955.  
  1956. procedure TSpreadSheet.FixBlockOverWrite(Block: TBlock);
  1957. { Updates the overwrite information of a block of cells
  1958.   IMPORTANT: No memory checking is done since it is assumed that no
  1959.              cells were added to the block being updated }
  1960. var
  1961.   CP, D : PCell;
  1962. begin
  1963.   with CellHash do
  1964.   begin
  1965.     CP := FirstItem;
  1966.     while CP <> NIL do
  1967.     begin
  1968.       if Block.CellInBlock(CP^.Loc) then
  1969.       begin
  1970.         OverWriteHash.Delete(CP^.Loc, CellHash, FormatHash, WidthHash,
  1971.           LastPos, MaxCols, GetColWidth, DisplayFormulas, ChangeNo);
  1972.         OverwriteHash.Add(CP, CellHash, FormatHash, WidthHash, LastPos,
  1973.           MaxCols, GetColWidth, DisplayFormulas, ChangeNo);
  1974.       end; {...if Block.CellInBlock(CP^.Loc) }
  1975.       CP := NextItem;
  1976.     end; {...while CP <> NIL}
  1977.   end; {...with CellHash }
  1978. end; {...TSpreadSheet.FixBlockOverWrite }
  1979.  
  1980.  
  1981. function TSpreadsheet.FixOverWrite: Boolean;
  1982. { Updates the overwrite information for each cell in the spreadsheet }
  1983. var
  1984.   CP, D : PCell;
  1985. begin
  1986.   FixOverWrite := False;
  1987.   with CellHash do
  1988.   begin
  1989.     CP := FirstItem;
  1990.     while CP <> NIL do
  1991.     begin
  1992.       if not OverwriteHash.Add(CP, CellHash, FormatHash, WidthHash, LastPos,
  1993.          MaxCols, GetColWidth, DisplayFormulas, ChangeYes) then
  1994.       begin
  1995.         CellHash.Delete(CP^.Loc, D);
  1996.         Dispose(D, Done);
  1997.         Exit;
  1998.       end; {...if not OverwriteHash.Add }
  1999.       CP := NextItem;
  2000.     end; {...while CP <> NIL }
  2001.   end; {...with CellHash }
  2002.   FixOverWrite := True;
  2003. end; {...TSpreadSheet.FixOverWrite }
  2004.  
  2005.  
  2006. procedure TSpreadSheet.FormatDefault;
  2007. { Clears the custom assigned formats of a block of cells }
  2008. var
  2009.   Block : TBlock;
  2010. begin
  2011.   with Block do
  2012.   begin
  2013.     if BlockOn then
  2014.       begin
  2015.         Start := CurrBlock^.Start;
  2016.         Stop := CurrBlock^.Stop;
  2017.       end {...if BlockOn }
  2018.     else
  2019.       begin
  2020.         Start := CurrPos;
  2021.         Stop := CurrPos;
  2022.       end; {...if/else }
  2023.   end; {...with Block }
  2024.   if not FormatHash.Delete(Block.Start, Block.Stop) then
  2025.     Exit;
  2026.   SetChanged(ModifiedYes);
  2027.   FixBlockOverWrite(Block);
  2028.   Block.Stop.Col := ScreenBlock^.Stop.Col;
  2029.   DisplayBlock(Block);
  2030. end; {...TSpreadSheet.FormatDefault }
  2031.  
  2032.  
  2033. function TSpreadSheet.FStringSituationColor(P: CellPos; var CP: PCell;
  2034.                       var HasError, ColorFound: Boolean): Byte;
  2035. { Returns situation especific colors of the string to be displayed in the
  2036.   screen (for example: highlighted cell color, cell in block color, etc). }
  2037.  
  2038.   function DisplayErrorColor: Boolean;
  2039.   { This function determines if the cell must be displayed in error color.
  2040.     When the cell is a formula cell and DisplayFormulas mode is on, even
  2041.     though HasError may return true, the cell should not be displayed
  2042.     in error color }
  2043.   begin
  2044.     DisplayErrorColor := HasError and not (DisplayFormulas
  2045.                          and (CP^.CellType = ClFormula));
  2046.   end; {...DisplayErrorColor }
  2047.  
  2048. begin
  2049.   ColorFound := True;
  2050.   CP := CellHash.Search(P);
  2051.   HasError := CP^.HasError;
  2052.   if not SheetProtected or (SheetProtected and not UnlockedHash.Search(P)) then
  2053.     begin
  2054.       if BlockOn and (SameCellPos(P, CurrPos)) then
  2055.         begin
  2056.           if not DisplayErrorColor then
  2057.             FStringSituationColor := GetColor(13)
  2058.           else
  2059.             FStringSituationColor := GetColor(21);
  2060.         end {...if BlockOn and (SameCellPos(P, CurrPos)) }
  2061.       else if SameCellPos(P, CurrPos) then
  2062.         begin
  2063.           if not DisplayErrorColor then
  2064.             FStringSituationColor := GetColor(12)
  2065.           else
  2066.             FStringSituationColor := GetColor(20);
  2067.         end {...else if SameCellPos(P, CurrPos) }
  2068.       else if BlockOn and (CurrBlock^.CellInBlock(P)) then
  2069.         begin
  2070.           if not DisplayErrorColor then
  2071.             FStringSituationColor := GetColor(11)
  2072.           else
  2073.             FStringSituationColor := GetColor(19);
  2074.         end {...else if BlockOn and (CurrBlock^.CellInBlock(P)) }
  2075.       else
  2076.         if not DisplayErrorColor then
  2077.           ColorFound := False
  2078.         else
  2079.           FStringSituationColor := GetColor(18);
  2080.     end {...if not SheetProtected or ... }
  2081.   else
  2082.     begin
  2083.       if BlockOn and (SameCellPos(P, CurrPos)) then
  2084.         begin
  2085.           if not DisplayErrorColor then
  2086.             FStringSituationColor := GetColor(17)
  2087.           else
  2088.             FStringSituationColor := GetColor(25);
  2089.         end {...if BlockOn and (SameCellPos(P, CurrPos)) }
  2090.       else if SameCellPos(P, CurrPos) then
  2091.         begin
  2092.           if not DisplayErrorColor then
  2093.             FStringSituationColor := GetColor(16)
  2094.           else
  2095.             FStringSituationColor := GetColor(24);
  2096.         end {...else if SameCellPos(P, CurrPos) }
  2097.       else if BlockOn and (CurrBlock^.CellInBlock(P)) then
  2098.         begin
  2099.           if not DisplayErrorColor then
  2100.             FStringSituationColor := GetColor(15)
  2101.           else
  2102.             FStringSituationColor := GetColor(23);
  2103.         end {...else if BlockOn and (CurrBlock^.CellInBlock(P)) }
  2104.       else
  2105.          if not DisplayErrorColor then
  2106.            FStringSituationColor := GetColor(14)
  2107.          else
  2108.            FStringSituationColor := GetColor(22);
  2109.     end; {...if/else }
  2110. end; {...TSpreadSheet.FStringSituationColor }
  2111.  
  2112.  
  2113. procedure TSpreadSheet.GetFormat;
  2114. var
  2115.   Cancel, ValidFormat : Boolean;
  2116.   NewDecimalPlaces : Byte;
  2117.   Start, Stop : CellPos;
  2118.   NewCurrency: Char;
  2119.   F : FormatType;
  2120.   Code : Integer;
  2121.   Dialog : PDialog;
  2122.   ErrorString : String;
  2123.   Block: TBlock;
  2124.   Format : Word;
  2125. const
  2126.   CurrencyBit = $01;
  2127.   CommasBit = $02;
  2128.  
  2129.     procedure SetDialogFormatRec;
  2130.     { Determines the initial values for the format dialog's fields }
  2131.     var
  2132.       CellPtr : PCell;
  2133.     begin
  2134.       CellPtr := CellHash.Search(CurrPos);
  2135.       if CellPtr <> Empty then
  2136.         begin
  2137.           F := CellPtr^.Format(FormatHash, DisplayFormulas);
  2138.           with RFormat do
  2139.           begin
  2140.             NumberFormat := 0;
  2141.             Justification := (F shr JustShift) and JustPart;
  2142.             if (F and CurrencyPart) <> 0 then
  2143.               NumberFormat := NumberFormat or CurrencyBit;
  2144.             if (F and CommasPart) <> 0 then
  2145.               NumberFormat := NumberFormat or CommasBit;
  2146.             if ((F and DecPlacesPart) = 0) and
  2147.                not ((CellPtr^.CellType = ClValue) or ((CellPtr^.CellType =
  2148.                ClFormula)) and DisplayFormulas = True) then
  2149.               Str(DefaultDecimalPlaces, DecimalPlaces)
  2150.             else
  2151.               Str(F and DecPlacesPart, DecimalPlaces);
  2152.             if (F and CurrencyCharPart) <> 0 then
  2153.               CurrencyChar := Char((F and CurrencyCharPart) shr CurrencyShift)
  2154.             else
  2155.               CurrencyChar := Copy(DefaultCurrency, 2, 1);
  2156.           end; {...with RFormat }
  2157.         end {...if CellPtr <> Empty }
  2158.       else
  2159.         begin
  2160.           with RFormat do
  2161.           begin
  2162.             Justification := Ord(JLeft);
  2163.             NumberFormat := 0;
  2164.             Str(DefaultDecimalPlaces, DecimalPlaces);
  2165.             CurrencyChar := Copy(DefaultCurrency, 2, 1);
  2166.           end; {...with RFormat }
  2167.         end; {...if/else }
  2168.     end; {...SetDialogFormatRec }
  2169.  
  2170.     procedure GetValidFormat(Dialog: PDialog; var ValidFormat, Cancel: Boolean);
  2171.     { Returns ValidFormat as true is a valid format was entered }
  2172.     var
  2173.       SelectedCommand : Word;
  2174.     begin
  2175.       SelectedCommand := Desktop^.ExecView(Dialog);
  2176.       if SelectedCommand <> cmCancel then
  2177.         begin
  2178.           Dialog^.GetData(RFormat);
  2179.           val(RFormat.DecimalPlaces, NewDecimalPlaces, Code);
  2180.           if (NewDecimalPlaces > MaxDecimalPlaces) then
  2181.             ErrorString := ErrorString + GLStringList^.Get(sFormatError1Msg)
  2182.           else
  2183.             ValidFormat := True;
  2184.           if ((RFormat.NumberFormat and CurrencyBit) <> 0) then
  2185.           begin
  2186.             if not ((RFormat.CurrencyChar <> '') and
  2187.                (RFormat.CurrencyChar <> ' ')) then
  2188.             begin
  2189.               ErrorString := ErrorString +
  2190.                 GLStringList^.Get(sFormatError2Msg);
  2191.               ValidFormat := False;
  2192.             end; {...if not ((RFormat.CurrencyChar<>'') and... }
  2193.           end; {...if (RFormat.NumberFormat and CurrencyBit) <> 0) }
  2194.         end {...if SelectedCommand <> cmCancel }
  2195.       else
  2196.         begin
  2197.           Cancel := True;
  2198.           ValidFormat := True;
  2199.         end; {...if/else }
  2200.     end; {...GetValidFormat }
  2201.  
  2202. begin
  2203.   Cancel := False;
  2204.   ValidFormat := False;
  2205.   if BlockOn then
  2206.     begin
  2207.       Block.Start := CurrBlock^.Start;
  2208.       Block.Stop := CurrBlock^.Stop;
  2209.     end {...if BlockOn }
  2210.   else
  2211.     Block.Init(CurrPos);
  2212.   Dialog := PDialog(GLResFile^.Get('FormatDialog'));
  2213.   SetDialogFormatRec;
  2214.   Dialog^.SetData(RFormat);
  2215.   repeat
  2216.     ErrorString := GLStringList^.Get(sFormatErrorMsg);
  2217.     if (Application^.ValidView(Dialog) <> NIL) then
  2218.       GetValidFormat(Dialog, ValidFormat, Cancel)
  2219.     else
  2220.       Exit;
  2221.     if not ValidFormat then
  2222.       MessageBox(ErrorString, NIL, mfError+mfOkButton);
  2223.   until Cancel or ValidFormat;
  2224.   if not Cancel then
  2225.   begin
  2226.     Dialog^.GetData(RFormat);
  2227.     with RFormat do
  2228.     begin
  2229.       NewCurrency := CurrencyChar[1];
  2230.       Format := NewDecimalPlaces + (Justification shl JustShift) +
  2231.         (NumberFormat shl NumberFormatShift) + (Ord(NewCurrency) shl
  2232.         CurrencyShift);
  2233.       if not FormatHash.Add(Block.Start, Block.Stop, Format) then
  2234.         Exit;
  2235.       SetChanged(ModifiedYes);
  2236.       FixBlockOverWrite(Block);
  2237.       Block.Stop.Col := ScreenBlock^.Stop.Col;
  2238.       DisplayBlock(Block);
  2239.     end; {...with RFormat }
  2240.   end; {...else if not Cancel }
  2241.   Dispose(Dialog, Done);
  2242. end; {...TSpreadSheet.GetFormat }
  2243.  
  2244.  
  2245. function TSpreadSheet.GetPalette: PPalette;
  2246. const
  2247.   CPalette : string[Length(CSpreadSheet)] = CSpreadSheet;
  2248. begin
  2249.   GetPalette := @CPalette;
  2250. end; {...TSpreadSheet.GetPalette }
  2251.  
  2252.  
  2253. procedure TSpreadSheet.GoToCell;
  2254. { Moves the highlight cursor to a user defined cell }
  2255. var
  2256.   Cancel, CellEntered : Boolean;
  2257.   OldPos, Pos : CellPos;
  2258.   Dialog : PDialog;
  2259.   FormLen : Word;
  2260. begin
  2261.   Cancel := False;
  2262.   CellEntered := False;
  2263.   Dialog := PDialog(GLResFile^.Get('GoToDialog'));
  2264.   repeat
  2265.     if (Application^.ValidView(Dialog) <> NIL) then
  2266.       begin
  2267.         if Desktop^.ExecView(Dialog) <> cmCancel then
  2268.           begin
  2269.             Dialog^.GetData(RGoToCell);
  2270.             if not FormulaStart(RGoToCell.NewCell, 1, MaxCols, MaxRows, Pos,
  2271.                FormLen) then
  2272.               MessageBox(GLStringList^.Get(sInvalidCellMsg), NIL, mfError +
  2273.                 mfOKButton)
  2274.             else
  2275.               CellEntered := True;
  2276.           end {...if Desktop^.ExecView(Dialog) <> cmCancel }
  2277.         else
  2278.           Cancel := True;
  2279.       end {...if Application^.ValidView(Dialog) <> NIL }
  2280.     else
  2281.       Exit;
  2282.   until CellEntered or Cancel;
  2283.   if not Cancel then
  2284.   begin
  2285.     if not ScreenBlock^.CellInBlock(Pos) then
  2286.       begin
  2287.         CurrPos := Pos;
  2288.         ExtendCurrBlock(RedrawYes);
  2289.         SetScreenColStart(CurrPos.Col);
  2290.         SetScreenRowStart(CurrPos.Row);
  2291.         HScrollBar^.Value := ScreenBlock^.Start.Col;
  2292.         VScrollBar^.Value := ScreenBlock^.Start.Row;
  2293.         HScrollBar^.DrawView;
  2294.         VScrollBar^.DrawView;
  2295.         DrawView;
  2296.       end {...if not ScreenBlock^.CellinBlock(Pos) }
  2297.     else
  2298.       begin
  2299.         OldPos := CurrPos;
  2300.         CurrPos := Pos;
  2301.         MoveCell(OldPos);
  2302.       end; {...if/else }
  2303.   end; {...if not Cancel }
  2304.   Dispose(Dialog, Done);
  2305. end; {...TSpreadSheet.GoToCell }
  2306.  
  2307.  
  2308. procedure TSpreadSheet.HandleEvent(var Event: TEvent);
  2309. { Handles all spreadsheet related events }
  2310.  
  2311.   procedure CheckforClipBoardClose;
  2312.   { if the spreadsheet being closed is @self, it resets the clipboard }
  2313.   begin
  2314.     if ClipBoard.Active and (ClipBoard.SourceSpreadSheet = @Self) then
  2315.       ToggleClipBoardOff;
  2316.   end; {...CheckforClipBoardClose }
  2317.  
  2318.   procedure EscPressed;
  2319.   begin
  2320.     if BlockOn then
  2321.     begin
  2322.       ClearCurrBlock;
  2323.       DisplayCellData;
  2324.     end; {...if BlockOn }
  2325.     if ClipBoard.Active then
  2326.       ToggleClipBoardOff;
  2327.   end; {...EscPressed }
  2328.  
  2329. begin
  2330.   case Event.What of
  2331.     evKeyDown :
  2332.       begin
  2333.         if ClipBoard.Active and ((Event.KeyCode = kbDel) or
  2334.            (Event.CharCode in [Chr(32)..Chr(255)])) then
  2335.           ToggleClipBoardOff;
  2336.         KeyPressed := True;
  2337.         case Event.KeyCode of
  2338.           kbCtrlLeft  : MovePgLeft;
  2339.           kbCtrlRight : MovePgRight;
  2340.           kbDel       : EraseCellBlock(RemoveSingleCell);
  2341.           kbDown      : MoveDown;
  2342.           kbEnd       : ToggleEnd;
  2343.           kbEnter     : PasteCellBlock;
  2344.           kbEsc       : EscPressed;
  2345.           kbHome      : MoveHome;
  2346.           KbLeft      : MoveLeft;
  2347.           kbPgDn      : MovePgDown;
  2348.           kbPgUp      : MovePgUp;
  2349.           kbRight     : MoveRight;
  2350.           kbUp        : MoveUp;
  2351.         end; {...case Event.KeyCode }
  2352.         KeyPressed := False;
  2353.         if Event.CharCode in [Chr(32)..Chr(255)] then
  2354.            HandleInput(Event.CharCode, EditNo);
  2355.         ClearEvent(Event);
  2356.       end; {...case Event.What of evKeyDown }
  2357.     evMouseDown :
  2358.       begin
  2359.         if Event.Double then
  2360.           SetNameWithMouse(Event)
  2361.         else if not SelectColumn(Event) then
  2362.           begin
  2363.             LocateCursorWithMouse(Event);
  2364.             while MouseEvent(Event, evMouseMove + evMouseAuto) do
  2365.             begin
  2366.               Desktop^.Lock;
  2367.               DragCursorWithMouse(Event);
  2368.               Desktop^.Unlock;
  2369.             end; {...while MouseEvent(Event, evMouseMove + evMouseAuto) }
  2370.           end; {...else if not SelectColumn(Event) }
  2371.       end; {...case Event.What of evMouseDown }
  2372.  
  2373.     evCommand:
  2374.       begin
  2375.         if ClipBoard.Active and not (Event.Command in [cmNewSheet, cmPaste,
  2376.            cmNext, cmPrev, cmZoom, cmResize, cmClose]) then
  2377.           ToggleClipBoardOff;
  2378.         case Event.Command of
  2379.           cmCut              : MoveCellBlock;
  2380.           cmPaste            : PasteCellBlock;
  2381.           cmClose            : CheckforClipBoardClose;
  2382.           cmCopy             : CopyCellBlock;
  2383.           cmClear            : EraseCellBlock(RemoveBlock);
  2384.           cmPrintSheet       : Print;
  2385.           cmChangeColWidth   : ChangeColWidth;
  2386.           cmDeleteColumns    : DeleteColumns;
  2387.           cmDeleteRows       : DeleteRows;
  2388.           cmInsertColumns    : InsertColumns;
  2389.           cmInsertRows       : InsertRows;
  2390.           cmEditCell         : HandleInput('', EditYes);
  2391.           cmFormatCells      : GetFormat;
  2392.           cmFormatDefault    : FormatDefault;
  2393.           cmGoToCell         : GoToCell;
  2394.           cmRecalc           : Recalc(DisplayYes);
  2395.           cmToggleAutoCalc   : ToggleAutoCalc;
  2396.           cmToggleFormulas   : ToggleFormulaDisplay;
  2397.           cmChangeColHeaders : ChangeColHeaders;
  2398.           cmDeleteColHeaders :
  2399.             begin
  2400.               DeleteColHeaders(CurrBlock);
  2401.               DisplayCols;
  2402.             end; {...case Event.Command of cmDeleteColHeaders }
  2403.           cmToggleHeaders    : ToggleDisplayHeaders;
  2404.           cmToggleProtection : SetProtection(not SheetProtected, True);
  2405.           cmSetLocked        : SetLocked;
  2406.           cmSetUnlocked      : SetUnlocked;
  2407.           cmSortData         : SortData;
  2408.         end; {...case Event.Command }
  2409.     end; {...case Event.What of evCommand }
  2410.   end; {...case Event.What }
  2411.   TScroller.HandleEvent(Event);
  2412. end; {...TSpreadSheet.HandleEvent }
  2413.  
  2414.  
  2415. procedure TSpreadSheet.HandleInput(FirstChar: String; Editing: Boolean);
  2416. { Gets data from the user, validates it and creates the corresponding cell }
  2417. var
  2418.   Deleted, FirstEdit, Good : Boolean;
  2419.   CurrentPos  : CellPos;
  2420.   CellValue : Extended;
  2421.   Code : Integer;
  2422.   InputLine   : PSheetInputLine;
  2423.   StringInput : PString;
  2424.   R : TRect;
  2425.  
  2426.     procedure DisplayEnteredString;
  2427.     var
  2428.       B : TDrawBuffer;
  2429.     begin
  2430.       with ContentsArea do
  2431.       begin
  2432.         MoveChar(B, ' ', Attrib, ScreenCols);
  2433.         Writeline(UpperLeft.Col, UpperLeft.Row, ScreenCols, 1, B);
  2434.         MoveStr(B, Copy(StringInput^, Succ(InputLine^.FirstPos),
  2435.           Min((Length(StringInput^) - InputLine^.FirstPos), ScreenCols)),
  2436.           Attrib);
  2437.         Writeline (Succ(UpperLeft.Col), UpperLeft.Row,
  2438.           Min((Length(StringInput^) - InputLine^.FirstPos), ScreenCols), 1, B);
  2439.       end; {...with ContenstArea }
  2440.     end; {...DisplayEnteredString }
  2441.  
  2442. begin
  2443.   if not SheetProtected or (SheetProtected and
  2444.      UnlockedHash.Search(CurrPos)) then
  2445.     begin
  2446.       Good := True;
  2447.       TrackCursor;
  2448.       GetMem(StringInput, 255);
  2449.       if StringInput = NIL then
  2450.       begin
  2451.         Application^.OutofMemory;
  2452.         Exit;
  2453.       end; {...if StringInput = NIL }
  2454.       GoToEnd := True;
  2455.       ToggleEnd;
  2456.       with ContentsArea do
  2457.       begin
  2458.         R.Assign(Succ(UpperLeft.Col), Succ(UpperLeft.Row),
  2459.           Succ(LowerRight.Col), Succ(LowerRight.Row));
  2460.         Inc(R.B.X);
  2461.         Inc(R.B.Y);
  2462.         if Editing then
  2463.         begin
  2464.           CellHash.Search(CurrPos)^.EditString(MaxDecimalPlaces, StringInput^);
  2465.           FirstChar := StringInput^;
  2466.         end; {...if Editing }
  2467.         InputLine := PSheetInputLine(GLResFile^.Get('InputLine'));
  2468.         InputLine^.SetBounds(R);
  2469.         if Editing then
  2470.           InputLine^.SetData(FirstChar)
  2471.         else
  2472.           begin
  2473.             InputLine^.Data^ := FirstChar;
  2474.             Inc(InputLine^.CurPos);
  2475.           end; {...if/else }
  2476.         FirstEdit := True;
  2477.         Parser^.Init(@CellHash, StringInput, MaxCols, MaxRows);
  2478.         repeat
  2479.           if FirstEdit then
  2480.             Owner^.ExecView(InputLine)
  2481.           else
  2482.             begin
  2483.               InputLine^.CurPos := Pred(Parser^.Position);
  2484.               if InputLine^.CurPos < (InputLine^.Size.X - 2) then
  2485.                 InputLine^.FirstPos := 0
  2486.               else
  2487.                 InputLine^.FirstPos := Succ(InputLine^.CurPos -
  2488.                   (InputLine^.Size.X - 2));
  2489.               Owner^.ExecView(InputLine);
  2490.             end; {...if/else }
  2491.           InputLine^.GetData(StringInput^);
  2492.           if Length(StringInput^) > 0 then
  2493.           begin
  2494.             DisplayEnteredString;
  2495.             Parser^.Parse;
  2496.             if Parser^.TokenError = 0 then
  2497.             begin
  2498.               DeleteCell(CurrPos, Deleted);
  2499.               if Parser^.CType = ClFormula then
  2500.                 StringInput^ := UpperCase(StringInput^);
  2501.               Good := AddCell (Parser^.CType, CurrPos, Parser^.ParseError,
  2502.                 Parser^.ParseValue, StringInput^);
  2503.             end; {...if Parser^.TokenError = 0 }
  2504.           end; {...if Length(StringInput^) > 0 }
  2505.           FirstEdit := False;
  2506.         until (Length(StringInput^) = 0) or (Parser^.TokenError = 0) or
  2507.           not Good;
  2508.         if (Length(StringInput^) > 0) and Good then
  2509.         begin
  2510.           SetChanged(ModifiedYes);
  2511.           if AutoCalc then
  2512.             Recalc(DisplayYes);
  2513.           CurrentPos := CurrPos;
  2514.           DoAfterEndInput;
  2515.           for CurrentPos.Col := CurrPos.Col to ScreenBlock^.Stop.Col do
  2516.             DisplayCell(CurrentPos);
  2517.         end; {...if (Length(StringInput^) > 0) and Good }
  2518.       end; {...with ContentsArea }
  2519.       Dispose(InputLine, Done);
  2520.       FreeMem(StringInput, 255);
  2521.       DisplayCellData;
  2522.     end {...if not SheetProtected or ... }
  2523.   else
  2524.     MessageBox(GLStringList^.Get(sCellsProtectedMsg), NIL, mfInformation +
  2525.       mfOKButton);
  2526. end; {...TSpreadSheet.HandleInput }
  2527.  
  2528.  
  2529. procedure TSpreadSheet.InitCurrPos;
  2530. { Locates the cursor in the first column and in the first row }
  2531. begin
  2532.   CurrPos.Col := 1;
  2533.   CurrPos.Row := 1;
  2534. end; {...InitCurrPos }
  2535.  
  2536.  
  2537. procedure TSpreadSheet.InsertColToHash(Block: TBlock;
  2538.                        Columns, StartInsCol: Word; var Deleted: Boolean);
  2539. { Updates all the hash tables after a column or group of columns is inserted }
  2540. var
  2541.   Start, Stop : CellPos;
  2542.   H : HashItemPtr;
  2543.   CellPtr : PCell;
  2544.   Col : Word;
  2545. begin
  2546.   DeleteBlock(Block, Deleted);
  2547.   with CellHash do
  2548.   begin
  2549.     CellPtr := FirstItem;
  2550.     while CellPtr <> NIL do
  2551.     begin
  2552.       with CellPtr^ do
  2553.       begin
  2554.         if (CellPtr^.ShouldUpdate) then
  2555.           FixFormulaCol(CellPtr, opInsert, StartInsCol, Columns, MaxCols,
  2556.             MaxRows);
  2557.       end; {...with CellPtr^ }
  2558.       CellPtr := NextItem;
  2559.     end; {...while CellPtr <> NIL }
  2560.   end; {...with CellHash }
  2561.  
  2562.   for Col := (MaxCols - Pred(Columns)) to MaxCols do
  2563.     WidthHash.Delete(Col);
  2564.   with WidthHash do
  2565.   begin
  2566.     H := FirstItem;
  2567.     while H <> NIL do
  2568.     begin
  2569.       if WordPTr(@H^.Data)^ >= StartInsCol then
  2570.         Inc(WordPtr(@H^.Data)^, Columns);
  2571.       H := NextItem;
  2572.     end; {...with H <> NIL }
  2573.   end; {...with WidthHash }
  2574.  
  2575.   Stop.Col := Block.Stop.Col;
  2576.   Stop.Row := MaxInt;
  2577.   FormatHash.Delete(Block.Start, Stop);
  2578.   with FormatHash do
  2579.   begin
  2580.     H := FirstItem;
  2581.     while H <> NIL do
  2582.     begin
  2583.       Move(H^.Data, Start, SizeOf(Start));
  2584.       Move(H^.Data[SizeOf(CellPos)], Stop, SizeOf(Stop));
  2585.       if Start.Col >= StartInsCol then
  2586.       begin
  2587.         Inc(Start.Col, Columns);
  2588.         Move(Start, H^.Data, Sizeof(Start));
  2589.       end; {...if Start.Col >= StartInsCol }
  2590.       if Stop.Col >= StartInsCol then
  2591.       begin
  2592.         Inc(Stop.Col, Columns);
  2593.         Move(Stop, H^.Data[Sizeof(CellPos)], Sizeof(Stop));
  2594.       end; {...if Stop.Col >= StartInsCol }
  2595.       H := NextItem;
  2596.     end; {...while H <> NIL }
  2597.   end; {...with FormatHash }
  2598.  
  2599.   DeleteColHeaders(@Block);
  2600.   with ColHeadersHash do
  2601.   begin
  2602.     for Col := (MaxCols - Pred(Columns)) to MaxCols do
  2603.       Delete(Col);
  2604.     H := FirstItem;
  2605.     while H <> NIL do
  2606.     begin
  2607.       if WordPTr(@H^.Data)^ >= StartInsCol then
  2608.         Inc(WordPtr(@H^.Data)^, Columns);
  2609.        H := NextItem;
  2610.     end; {...with H <> NIL }
  2611.   end; {...with ColHeadersHash }
  2612.  
  2613.   Stop.Col := Block.Stop.Col;
  2614.   Stop.Row := MaxInt;
  2615.   UnlockedHash.Delete(Block.Start, Stop);
  2616.   with UnlockedHash do
  2617.   begin
  2618.     H := FirstItem;
  2619.     while H <> NIL do
  2620.     begin
  2621.       Move(H^.Data, Start, SizeOf(Start));
  2622.       Move(H^.Data[SizeOf(CellPos)], Stop, SizeOf(Stop));
  2623.       if Start.Col >= StartInsCol then
  2624.       begin
  2625.         Inc(Start.Col, Columns);
  2626.         Move(Start, H^.Data, Sizeof(Start));
  2627.       end; {...if Start.Col >= StartInsCol }
  2628.       if Stop.Col >= StartInsCol then
  2629.       begin
  2630.         Inc(Stop.Col, Columns);
  2631.         Move(Stop, H^.Data[Sizeof(CellPos)], Sizeof(Stop));
  2632.       end; {...if Stop.Col >= StartInsCol }
  2633.       H := NextItem;
  2634.     end; {...while H <> NIL }
  2635.   end; {...with UnlockedHash }
  2636. end; {...TSpreadSheet.InsertColToHash }
  2637.  
  2638.  
  2639. procedure TSpreadSheet.InsertColumns;
  2640. { Inserts one or more columns at the current position }
  2641. var
  2642.   Dialog : PDialog;
  2643.   Deleted : Boolean;
  2644.   Pos, Start, Stop: CellPos;
  2645.   F : File;
  2646.   H : HashItemPtr;
  2647.   CellPtr : PCell;
  2648.   Block : TBlock;
  2649.   Column, Columns, StartInsCol : Word;
  2650. begin
  2651.   Block.Start.Col := 0;
  2652.   Block.Start.Row := 0;
  2653.   Block.Stop.Col := 0;
  2654.   Block.Stop.Row := 0;
  2655.   Deleted := False;
  2656.   if BlockOn then
  2657.     begin
  2658.       Columns := Succ(CurrBlock^.Stop.Col - CurrBlock^.Start.Col);
  2659.       StartInsCol := CurrBlock^.Start.Col;
  2660.       if Pred(LastPos.Col + Columns) >= MaxCols then
  2661.       begin
  2662.         with Block do
  2663.         begin
  2664.           Start.Col := MaxCols - Pred(Columns);
  2665.           Start.Row := 1;
  2666.           Stop.Col := MaxCols;
  2667.           Stop.Row := LastPos.Row;
  2668.         end; {...with Block }
  2669.         LastPos.Col := MaxCols;
  2670.       end {...if Pred(LastPos.Col + Columns) >= MaxCols }
  2671.     end {...if BlockOn }
  2672.   else
  2673.     begin
  2674.       Columns := 1;
  2675.       StartInsCol := CurrPos.Col;
  2676.       if LastPos.Col = MaxCols then
  2677.       begin
  2678.         with Block do
  2679.         begin
  2680.           Start.Col := MaxCols;
  2681.           Start.Row := 1;
  2682.           Stop.Col := MaxCols;
  2683.           Stop.Row := LastPos.Row;
  2684.         end; {...with Block do }
  2685.       end {...if LastPos.Col = MaxCols }
  2686.     end; {...if/else }
  2687.   Dialog := PDialog(GLResFile^.Get('UpdatingTablesDialog'));
  2688.   if Application^.ValidView(Dialog) <> NIL then
  2689.     Desktop^.Insert(Dialog)
  2690.   else
  2691.     Exit;
  2692.   InsertColToHash(Block, Columns, StartInsCol, Deleted);
  2693.   StoreTablesToTempFile;
  2694.   DoneHashTables;
  2695.   Pos.Col := StartInsCol;
  2696.   Pos.Row := 0;
  2697.   LoadTablesFromTempFile(Pos, 0, Columns);
  2698.   Assign(F, GLStringList^.Get(sTempFileName));
  2699.   Erase(F);
  2700.   LastPos.Col := Min(LastPos.Col + Columns, MaxCols);
  2701.   if LastPos.Col = MaxCols then
  2702.     Pos.Col := MaxCols
  2703.   else
  2704.     begin
  2705.       if BlockOn then
  2706.         Pos.Col := Pred(StartInsCol + Columns) + Columns
  2707.       else
  2708.         Pos.Col := StartInsCol + Columns;
  2709.     end; {...if/else }
  2710.   if Deleted then
  2711.     Pos.Row := LastPos.Row
  2712.   else
  2713.     Pos.Row := 1;
  2714.   FindLastPos(Pos);
  2715.   SetChanged(ModifiedYes);
  2716.   FixOverWrite;
  2717.   SetScreenColStart(ScreenBlock^.Start.Col);
  2718.   if AutoCalc then
  2719.     Recalc(DisplayNo);
  2720.   Desktop^.Delete(Dialog);
  2721.   Dispose(Dialog, Done);
  2722.   DrawView;
  2723. end; {...TSpreadSheet.InsertColumns }
  2724.  
  2725.  
  2726. procedure TSpreadSheet.InsertRowToHash(Block: TBlock; Rows, StartInsRow: Word;
  2727.   var Deleted: Boolean);
  2728. { Updates all the hash tables after a row or group of rows is deleted }
  2729. var
  2730.   Start, Stop : CellPos;
  2731.   H : HashItemPtr;
  2732.   CellPtr : PCell;
  2733. begin
  2734.   DeleteBlock(Block, Deleted);
  2735.   with CellHash do
  2736.   begin
  2737.     CellPtr := FirstItem;
  2738.     while CellPtr <> NIL do
  2739.     begin
  2740.       with CellPtr^ do
  2741.       begin
  2742.         if (CellPtr^.ShouldUpdate) then
  2743.           FixFormulaRow(CellPtr, opInsert, StartInsRow, Rows, MaxCols,
  2744.             MaxRows);
  2745.       end; {...with CellPtr^ }
  2746.       CellPtr := NextItem;
  2747.     end; {...while CellPtr <> NIL }
  2748.   end; {...with CellHash }
  2749.  
  2750.   Stop.Col := MaxInt;
  2751.   Stop.Row := Block.Stop.Row;
  2752.   FormatHash.Delete(Block.Start, Stop);;
  2753.   with FormatHash do
  2754.   begin
  2755.     H := FirstItem;
  2756.     while H <> NIL do
  2757.     begin
  2758.       Move(H^.Data, Start, SizeOf(Start));
  2759.       Move(H^.Data[SizeOf(CellPos)], Stop, SizeOf(Stop));
  2760.       if Start.Row >= StartInsRow then
  2761.       begin
  2762.         Inc(Start.Row, Rows);
  2763.         Move(Start, H^.Data, Sizeof(Start));
  2764.       end; {...if Start.Row >= StartInsRow }
  2765.       if Stop.Row >= StartInsRow then
  2766.       begin
  2767.         Inc(Stop.Row, Rows);
  2768.         Move(Stop, H^.Data[Sizeof(CellPos)], Sizeof(Stop));
  2769.       end; {...if Stop.Row >= StartInsRow }
  2770.       H := NextItem;
  2771.     end; {...while H <> NIL }
  2772.   end; {...with FormatHash }
  2773.  
  2774.   Stop.Col := MaxInt;
  2775.   Stop.Row := Block.Stop.Row;
  2776.   UnlockedHash.Delete(Block.Start, Stop);
  2777.   with UnlockedHash do
  2778.   begin
  2779.     H := FirstItem;
  2780.     while H <> NIL do
  2781.     begin
  2782.       Move(H^.Data, Start, SizeOf(Start));
  2783.       Move(H^.Data[SizeOf(CellPos)], Stop, SizeOf(Stop));
  2784.       if Start.Row >= StartInsRow then
  2785.       begin
  2786.         Inc(Start.Row, Rows);
  2787.         Move(Start, H^.Data, Sizeof(Start));
  2788.       end; {...if Start.Row >= StartInsRow }
  2789.       if Stop.Row >= StartInsRow then
  2790.       begin
  2791.         Inc(Stop.Row, Rows);
  2792.         Move(Stop, H^.Data[Sizeof(CellPos)], Sizeof(Stop));
  2793.       end; {...if Stop.Row >= StartInsRow }
  2794.       H := NextItem;
  2795.     end; {...while H <> NIL }
  2796.   end; {...with UnlockedHash }
  2797. end; {...TSpreadSheet.InsertRowToHash }
  2798.  
  2799.  
  2800. procedure TSpreadSheet.InsertRows;
  2801. { Inserts one or more rows at the current position }
  2802. var
  2803.   Dialog : PDialog;
  2804.   Deleted : Boolean;
  2805.   Pos, Start, Stop: CellPos;
  2806.   F : File;
  2807.   H : HashItemPtr;
  2808.   CellPtr : PCell;
  2809.   Block : TBlock;
  2810.   Rows, StartInsRow : Word;
  2811. begin
  2812.   Block.Start.Col := 0;
  2813.   Block.Start.Row := 0;
  2814.   Block.Stop.Col := 0;
  2815.   Block.Stop.Row := 0;
  2816.   Deleted := False;
  2817.   if BlockOn then
  2818.     begin
  2819.       Rows := Succ(CurrBlock^.Stop.Row - CurrBlock^.Start.Row);
  2820.       StartInsRow := CurrBlock^.Start.Row;
  2821.       if Pred(LastPos.Row + Rows) >= MaxRows then
  2822.         begin
  2823.           with Block do
  2824.           begin
  2825.             Start.Col := 1;
  2826.             Start.Row := MaxRows - Pred(Rows);
  2827.             Stop.Col := LastPos.Col;
  2828.             Stop.Row := MaxRows;
  2829.           end; {...with Block }
  2830.           LastPos.Row := MaxRows;
  2831.         end {...if Pred(LastPos.Row + Rows) >= MaxRows }
  2832.     end {...if BlockOn }
  2833.   else
  2834.     begin
  2835.       Rows := 1;
  2836.       StartInsRow := CurrPos.Row;
  2837.       if LastPos.Row = MaxRows then
  2838.       begin
  2839.         with Block do
  2840.         begin
  2841.           Start.Col := 1;
  2842.           Start.Row := MaxRows;
  2843.           Stop.Col := LastPos.Col;
  2844.           Stop.Row := MaxRows;
  2845.         end; {...with Block }
  2846.       end {...if LastPos.Row = MaxRows }
  2847.     end; {...if/else }
  2848.   Dialog := PDialog(GLResFile^.Get('UpdatingTablesDialog'));
  2849.   if Application^.ValidView(Dialog) <> NIL then
  2850.     Desktop^.Insert(Dialog)
  2851.   else
  2852.     Exit;
  2853.   InsertRowToHash(Block, Rows, StartInsRow, Deleted);
  2854.   StoreTablesToTempFile;
  2855.   DoneHashTables;
  2856.   Pos.Col := 0;
  2857.   Pos.Row := StartInsRow;
  2858.   LoadTablesFromTempFile(Pos, Rows, 0);
  2859.   Assign(F, GLStringList^.Get(sTempFileName));
  2860.   Erase(F);
  2861.   if Pred(LastPos.Row+Rows) < MaxRows then
  2862.      LastPos.Row := Min(LastPos.Row + Rows, MaxRows);
  2863.   if LastPos.Row = MaxRows then
  2864.      Pos.Row := MaxRows
  2865.   else
  2866.     begin
  2867.       if BlockOn then
  2868.         Pos.Row := Pred(StartInsRow + Rows) + Rows
  2869.       else
  2870.         Pos.Row := StartInsRow + Rows;
  2871.     end; {...if/else }
  2872.   if Deleted then
  2873.     Pos.Col := LastPos.Col
  2874.   else
  2875.     Pos.Col := 1;
  2876.   FindLastPos(Pos);
  2877.   SetChanged(ModifiedYes);
  2878.   FixOverWrite;
  2879.   if AutoCalc then
  2880.     Recalc(DisplayNo);
  2881.   Desktop^.Delete(Dialog);
  2882.   Dispose(Dialog, Done);
  2883.   DrawView;
  2884. end; {...TSpreadSheet.InsertRows }
  2885.  
  2886.  
  2887. constructor TSpreadSheet.Load(var S: TStream);
  2888. { Loads the spreadsheet object from a stream }
  2889. var
  2890.   R : TRect;
  2891.   AdjustPos : CellPos;
  2892.   FileHeader : String[Length(OOGridFileHeader)];
  2893. const
  2894.   MinRowsToDisplay = 2;
  2895. begin
  2896.   AdjustPos.Col := 0;
  2897.   AdjustPos.Row := 0;
  2898.   TScroller.Load(S);
  2899.   S.Read(FileHeader, SizeOf(FileHeader));
  2900.   if FileHeader <> OOGridFileHeader then
  2901.   begin
  2902.     S.Error(stInvalidFormatError, 0);
  2903.     Exit;
  2904.   end; {...if FileHeader <> OOGridFileHeader }
  2905.   S.Read(EmptyRowsAtTop, SizeOf(EmptyRowsAtTop));
  2906.   S.Read(EmptyRowsAtBottom ,SizeOf(EmptyRowsAtBottom));
  2907.   S.Read(MaxCols, SizeOf(MaxCols));
  2908.   S.Read(MaxRows, SizeOf(MaxRows));
  2909.   S.Read(DefaultColWidth, SizeOf(DefaultColWidth));
  2910.   S.Read(DefaultDecimalPlaces, SizeOf(DefaultDecimalPlaces));
  2911.   S.Read(MaxDecimalPlaces, SizeOf(MaxDecimalPlaces));
  2912.   S.Read(DefaultCurrency, SizeOf(DefaultCurrency));
  2913.   S.Read(LastPos, SizeOf(LastPos));
  2914.   LoadHashTables(S, AdjustPos, 0, 0);
  2915.   if S.Status <> 0 then
  2916.     Exit;
  2917.   if not FixOverWrite then
  2918.   begin
  2919.     S.Error(stNoMemoryError, 0);
  2920.     Exit;
  2921.   end; {...if not FixOverWrite }
  2922.   ScreenBlock := PBlock(S.Get);
  2923.   S.Read(CurrPos, SizeOf(CurrPos));
  2924.   S.Read(BlockOn, SizeOf(BlockOn));
  2925.   CurrBlock := PBlock(S.Get);
  2926.   if S.Status <> 0 then
  2927.     Exit;
  2928.   S.Read(DisplayFormulas, SizeOf(DisplayFormulas));
  2929.   S.Read(AutoCalc, SizeOf(AutoCalc));
  2930.   S.Read(DisplayHeaders, SizeOf(DisplayHeaders));
  2931.   S.Read(SheetProtected, SizeOf(SheetProtected));
  2932.   if S.Status <> 0 then
  2933.     Exit;
  2934.   EnableCommands([cmRecalc, cmToggleAutoCalc, cmToggleFormulas, cmEditCell,
  2935.     cmGoToCell, cmChangeColWidth, cmDeleteColumns, cmInsertColumns,
  2936.     cmDeleteRows, cmInsertRows, cmFormatCells, cmFormatDefault, cmClear,
  2937.     cmCopy, cmPaste, cmCut, cmToggleHeaders, cmToggleProtection,
  2938.     cmSetUnLocked, cmSetLocked, cmSortData, cmPrintSheet]);
  2939.   SetProtection(SheetProtected, False);
  2940.   RowNumberSpace := 6;
  2941.   MaxColWidth := ScreenCols - RowNumberSpace;
  2942.   MaxScreenCols := MaxColWidth div DefaultMinColWidth;
  2943.   GetMem(ColStart, MaxScreenCols);
  2944.   if ColStart = NIL then
  2945.   begin
  2946.     S.Error(stNoMemoryError, 0);
  2947.     Exit;
  2948.   end; {...if ColStart = NIL }
  2949.   OldCurrPos := CurrPos;
  2950.   GetExtent(R);
  2951.   Inc(R.A.Y, EmptyRowsAtTop);
  2952.   Dec(R.B.Y, EmptyRowsAtBottom);
  2953.   SetAreas(R);
  2954.   Recalc(DisplayNo);
  2955. end; {...TSpreadSheet.Load }
  2956.  
  2957.  
  2958. procedure TSpreadSheet.LoadDelimited(FileName: PathStr);
  2959. { This method imports a comma delimited file of a certain format and
  2960.   is intended only as an example of how to import comma delimited files.
  2961.   This method must be overridden if you wish to import delimited files of
  2962.   different formats }
  2963. var
  2964.   F : Text;
  2965.   S, SAdd : String;
  2966.   V : Extended;
  2967.   Counter, Code : Integer;
  2968.   Pos : CellPos;
  2969.   NotString : Boolean;
  2970.   TempStream : TBufStream;
  2971.  
  2972. const
  2973.   CR = CHR(13);
  2974.   AL = CHR(10);
  2975.  
  2976.   procedure CloseAndUpdateHash;
  2977.   begin
  2978.     Close(F);
  2979.     FixOverWrite;
  2980.     FindLastPos(LastPos);
  2981.     DrawView;
  2982.     LowMemSize := 4096 div 16;
  2983.     TempStream.Done;
  2984.   end; {...CloseAndUpdateHash }
  2985.  
  2986. begin
  2987.   LowMemSize := 5088 div 16;
  2988.   TempStream.Init(GLStringList^.Get(sTempFileName), stCreate, 1024);
  2989.   Assign(F, FileName);
  2990.   Reset(F);
  2991.   Pos.Row := 0;
  2992.   NotString := True;
  2993.   while not Eof(F) do
  2994.   begin
  2995.     Readln(F, S);
  2996.     Pos.Col := 1;
  2997.     Inc(Pos.Row);
  2998.     SAdd := '';
  2999.     for Counter := 1 to Length(S) do
  3000.     begin
  3001.       if ( S[Counter] in [','] ) and NotString then
  3002.       begin
  3003.         if SAdd <> '' then
  3004.         begin
  3005.           case Pos.Col of
  3006.             2..10,15 :
  3007.               begin
  3008.                 if not AddCell(ClText, Pos, False, 0, ' '+SAdd) then
  3009.                 begin
  3010.                   CloseAndUpdateHash;
  3011.                   Exit;
  3012.                 end; {...if not AddCell }
  3013.               end; {...case Pos.Col of 2..10, 15] }
  3014.  
  3015.             1, 11..14, 16 :
  3016.               begin
  3017.                 if SAdd[Length(SAdd)] = ' ' then
  3018.                   SAdd := Copy(SAdd, 1, Length(SAdd)-1);
  3019.                 val(SAdd, V, Code);
  3020.                 if not AddCell(ClValue, Pos, False, V, '') then
  3021.                 begin
  3022.                   CloseAndUpdateHash;
  3023.                   Exit;
  3024.                 end; {...if not AddCell }
  3025.               end; {...case Pos.Col of 1, 11..14, 16 }
  3026.           end; {...case Pos.Col }
  3027.           SAdd := '';
  3028.         end; {...if SAdd <> '' }
  3029.         Inc(Pos.Col);
  3030.       end; {...if ( S[Counter] in ',' ) and NotString }
  3031.       if S[Counter] = '"' then
  3032.         NotString := not NotString;
  3033.       if not (S[Counter] in ['"','$',',']) then
  3034.         SAdd := SAdd + S[Counter];
  3035.     end; {...for Counter }
  3036.     if SAdd <> '' then
  3037.     begin
  3038.       val(SAdd, V, Code);
  3039.       if not AddCell(ClValue, Pos, False, V, '') then
  3040.       begin
  3041.         CloseAndUpdateHash;
  3042.         Exit;
  3043.       end; {...if not AddCell }
  3044.       SAdd := '';
  3045.     end; {...if SAdd <> '' }
  3046.   end; {...while not Eof(F) }
  3047.   CloseAndUpdateHash;
  3048. end; {...TSpreadSheet.LoadDelimited }
  3049.  
  3050.  
  3051. procedure TSpreadSheet.LoadHashTables(var S: TStream; AdjustAfter: CellPos;
  3052.   RowAdjustment, ColAdjustment: Integer);
  3053. { Loads all the hash tables from a stream }
  3054. var
  3055.   TotalC, TotalF : LongInt;
  3056.   TotalW : Word;
  3057.   TotalHeaders : Word;
  3058.   TotalUnlocked : LongInt;
  3059. begin
  3060.   S.Read(TotalC, SizeOf(TotalC));
  3061.   S.Read(TotalW, SizeOf(TotalW));
  3062.   S.Read(TotalF, SizeOf(TotalF));
  3063.   S.Read(TotalHeaders, 2);
  3064.   S.Read(TotalUnlocked, SizeOf(TotalUnlocked));
  3065.   if not CellHash.Init(CellHashStart(TotalC)) then
  3066.   begin
  3067.     S.Error(stNoMemoryError, 0);
  3068.     Exit;
  3069.   end; {...if not CellHash.Init(CellHashStart(TotalC)) }
  3070.   if not WidthHash.Init(WidthHashStart, DefaultColWidth) then
  3071.   begin
  3072.     S.Error(stNoMemoryError, 0);
  3073.     Exit;
  3074.   end; {...if not WidthHash.Init(WidthHashStart, DefaultColWidth) }
  3075.   if not FormatHash.Init then
  3076.   begin
  3077.     S.Error(stNoMemoryError, 0);
  3078.     Exit;
  3079.   end; {...if not FormatHash.Init }
  3080.   if not OverWriteHash.Init(OverWriteHashStart) then
  3081.   begin
  3082.     S.Error(stNoMemoryError, 0);
  3083.     Exit;
  3084.   end; {...if not OverwriteHash.Init(OverwriteHashStart) }
  3085.   if not ColHeadersHash.Init(ColHeadersHashStart) then
  3086.   begin
  3087.     S.Error(stNoMemoryError, 0);
  3088.     Exit;
  3089.   end; {...if not ColHeadersHash.Init(ColHeadersHashStart) }
  3090.   if not UnlockedHash.Init then
  3091.   begin
  3092.     S.Error(stNoMemoryError, 0);
  3093.     Exit;
  3094.   end; {...if not UnlockedHash.Init }
  3095.   CellHash.Load(S, TotalC, AdjustAfter, RowAdjustment, ColAdjustment);
  3096.   if S.Status <> 0 then
  3097.     Exit;
  3098.   WidthHash.Load(S, TotalW);
  3099.   if S.Status <> 0 then
  3100.     Exit;
  3101.   FormatHash.Load(S, TotalF);
  3102.   if S.Status <> 0 then
  3103.     Exit;
  3104.   ColHeadersHash.Load(S, TotalHeaders);
  3105.   if S.Status <> 0 then
  3106.     Exit;
  3107.   UnlockedHash.Load(S, TotalUnlocked);
  3108. end; {...TSpreadSheet.LoadHashTables }
  3109.  
  3110.  
  3111. procedure TSpreadSheet.LoadTablesFromTempFile(AdjustAfter: CellPos;
  3112.   RowAdjustment, ColAdjustment: Integer);
  3113. { Loads the hash tables from the temporal file in disk }
  3114. var
  3115.   S : TDosStream;
  3116. begin
  3117.   S.Init(GLStringList^.Get(sTempFileName), stOpenRead);
  3118.   LoadHashTables(S, AdjustAfter, RowAdjustment, ColAdjustment);
  3119.   S.Done;
  3120. end; {...TSpreadSheet.LoadTablesFromTempFile }
  3121.  
  3122.  
  3123. procedure TSpreadSheet.LocateCursorWithMouse(Event: TEvent);
  3124. { Positions the highlight cursor in the position where the mouse was clicked }
  3125. var
  3126.   ColScrPos : Byte;
  3127.   OldPos : CellPos;
  3128.   Counter : Integer;
  3129.   Mouse : TPoint;
  3130. begin
  3131.   MakeLocal(Event.Where, Mouse);
  3132.   with ScreenBlock^ do
  3133.   begin
  3134.     if DisplayArea.PointInArea(Mouse.X, Mouse.Y) then
  3135.     begin
  3136.       CheckforDragging;
  3137.       OldPos := CurrPos;
  3138.       CurrPos.Row := YToRow(Mouse.Y);
  3139.       if (not NoBlankArea) and (BlankArea.PointInArea(Mouse.X, Mouse.Y)) then
  3140.         CurrPos.Col := Stop.Col
  3141.       else
  3142.         CurrPos.Col := XToCol(Mouse.X);
  3143.       MoveCell(OldPos);
  3144.     end; {...if DisplayArea.PointInArea(Mouse.X, Mouse.Y) }
  3145.   end; {...with ScreenBlock^ }
  3146. end; {...TSpreadSheet.LocateCursorWithMouse }
  3147.  
  3148.  
  3149. procedure TSpreadSheet.MoveCell(OldPos: CellPos);
  3150. { Moves the cursor from one place to another and extends the block if active }
  3151. begin
  3152.   Desktop^.Lock;
  3153.   ExtendCurrBlock(RedrawYes);
  3154.   if ScreenBlock^.CellInBlock(OldPos) then
  3155.     DisplayCell(OldPos);
  3156.   DisplayCell(CurrPos);
  3157.   DisplayCellData;
  3158.   Desktop^.Unlock;
  3159. end; {...TSpreadSheet.MoveCell}
  3160.  
  3161.  
  3162. procedure TSpreadSheet.MoveCellBlock;
  3163. { Activates the clipboard and sets it to indicate the block to be moved }
  3164. var
  3165.   Block : PBlock;
  3166. begin
  3167.   if BlockOn then
  3168.     begin
  3169.       if not CellsProtected(CurrBlock^) then
  3170.         begin
  3171.           New(Block, Init(CurrBlock^.Start));
  3172.           Block^.Stop := CurrBlock^.Stop;
  3173.           ToggleClipBoardOn(@Self, Block, BlockOn, opMove);
  3174.         end {...if not CellsProtected(CurrBlock^) }
  3175.       else
  3176.         MessageBox(GLStringList^.Get(sCellsProtectedMsg), NIL, mfInformation +
  3177.           mfOKButton);
  3178.     end {...if BlockOn}
  3179.   else
  3180.     begin
  3181.       if not SheetProtected or (SheetProtected and
  3182.          UnlockedHash.Search(CurrPos)) then
  3183.         begin
  3184.           New(Block, Init(CurrPos));
  3185.           Block^.Stop := CurrPos;
  3186.           ToggleClipBoardOn(@Self, Block, BlockOn, opMove);
  3187.         end {...if not SheetProtected or ... }
  3188.       else
  3189.         MessageBox(GLStringList^.Get(sCellsProtectedMsg), NIL, mfInformation +
  3190.           mfOKButton);
  3191.     end; {...if/else }
  3192. end;  {...TSpreadSheet.MoveCellBlock}
  3193.  
  3194.  
  3195. procedure TSpreadSheet.MoveDown;
  3196. { Moves the cursor one row down }
  3197. var
  3198.   OldPos : CellPos;
  3199. begin
  3200.   if CurrPos.Row < MaxRows then
  3201.   begin
  3202.     CheckForDragging;
  3203.     Desktop^.Lock;
  3204.     OldPos := CurrPos;
  3205.     if GoToEnd then
  3206.       begin
  3207.         CurrPos.Row := MaxRows;
  3208.         ToggleEnd;
  3209.       end {...if GoToEnd }
  3210.     else
  3211.       Inc(CurrPos.Row);
  3212.     if TrackCursor then
  3213.       UpdateScreenBlockDisplay
  3214.     else
  3215.       MoveCell(OldPos);
  3216.     Desktop^.Unlock;
  3217.   end; {...if CurrPos.Row < MaxRows }
  3218. end; {...TSpreadSheet.MoveDown }
  3219.  
  3220.  
  3221. procedure TSpreadSheet.MoveHome;
  3222. { Moves the cursor to the upper left corner of the spreadsheet }
  3223. var
  3224.   OldPos : CellPos;
  3225. begin
  3226.   Desktop^.Lock;
  3227.   CheckforDragging;
  3228.   OldPos := CurrPos;
  3229.   InitCurrPos;
  3230.   if TrackCursor then
  3231.     UpdateScreenBlockDisplay
  3232.   else
  3233.     MoveCell(OldPos);
  3234.   GoToEnd := True;
  3235.   ToggleEnd;
  3236.   Desktop^.Unlock;
  3237. end; {...TSpreadSheet.MoveHome }
  3238.  
  3239.  
  3240. procedure TSpreadSheet.MoveLeft;
  3241. { Moves the cursor one column left }
  3242. var
  3243.   OldPos : CellPos;
  3244. begin
  3245.   if CurrPos.Col > 1 then
  3246.   begin
  3247.     CheckForDragging;
  3248.     Desktop^.Lock;
  3249.     OldPos := CurrPos;
  3250.     if GoToEnd then
  3251.       begin
  3252.         CurrPos.Col := 1;
  3253.         ToggleEnd;
  3254.       end {...if GoToEnd }
  3255.     else
  3256.       Dec(CurrPos.Col);
  3257.     if TrackCursor then
  3258.       UpdateScreenBlockDisplay
  3259.     else
  3260.       MoveCell(OldPos);
  3261.     Desktop^.Unlock;
  3262.   end; {...if CurrPos.Col > 1 }
  3263. end; {...TSpreadSheet.MoveLeft }
  3264.  
  3265.  
  3266. procedure TSpreadSheet.MovePgDown;
  3267. { Moves the cursor one full page down }
  3268. var
  3269.   OldPos : CellPos;
  3270. begin
  3271.   if CurrPos.Row < MaxRows then
  3272.   begin
  3273.     CheckForDragging;
  3274.     Desktop^.Lock;
  3275.     OldPos := CurrPos;
  3276.     TrackCursor;
  3277.     CurrPos.Row := Min(MaxRows, CurrPos.Row + TotalRows);
  3278.     SetScreenRowStart(Min(MaxRows, Succ(ScreenBlock^.Stop.Row)));
  3279.     UpdateScreenBlockDisplay;
  3280.     Desktop^.Unlock;
  3281.   end; {...if CurrPos.Row < MaxRows }
  3282. end; {...TSpreadSheet.MovePgDown }
  3283.  
  3284.  
  3285. procedure TSpreadSheet.MovePgLeft;
  3286. { Moves the cursor one full page left }
  3287. var
  3288.   OldPos : CellPos;
  3289.   TotalCols : Byte;
  3290. begin
  3291.   if CurrPos.Col > 1 then
  3292.   begin
  3293.     CheckForDragging;
  3294.     Desktop^.Lock;
  3295.     OldPos := CurrPos;
  3296.     TotalCols := Succ(ScreenBlock^.Stop.Col - ScreenBlock^.Start.Col);
  3297.     SetScreenColStop(Max(1, Pred(ScreenBlock^.Start.Col)));
  3298.     CurrPos.Col := Max(ScreenBlock^.Start.Col, LongInt(CurrPos.Col) -
  3299.       TotalCols);
  3300.     UpdateScreenBlockDisplay;
  3301.     Desktop^.Unlock;
  3302.   end; {...if CurrPos.Col > 1 }
  3303. end; {...TSpreadSheet.MovePgLeft }
  3304.  
  3305.  
  3306. procedure TSpreadSheet.MovePgRight;
  3307. { Moves the cursor one full page right }
  3308. var
  3309.   OldPos : CellPos;
  3310.   TotalCols : Byte;
  3311. begin
  3312.   if CurrPos.Col < MaxCols then
  3313.   begin
  3314.     CheckForDragging;
  3315.     Desktop^.Lock;
  3316.     OldPos := CurrPos;
  3317.     TotalCols := Succ(ScreenBlock^.Stop.Col - ScreenBlock^.Start.Col);
  3318.     SetScreenColStart(Min(MaxCols, Succ(ScreenBlock^.Stop.Col)));
  3319.     CurrPos.Col := Min(ScreenBlock^.Stop.Col, LongInt(CurrPos.Col) +
  3320.       TotalCols);
  3321.     UpdateScreenBlockDisplay;
  3322.     Desktop^.Unlock;
  3323.   end; {...if CurrPos.Col < MaxCols }
  3324. end; {...TSpreadSheet.MovePgRight }
  3325.  
  3326.  
  3327. procedure TSpreadSheet.MovePgUp;
  3328. var
  3329.   OldPos, NewPos : CellPos;
  3330. begin
  3331.   if CurrPos.Row > 1 then
  3332.   begin
  3333.     CheckForDragging;
  3334.     Desktop^.Lock;
  3335.     OldPos := CurrPos;
  3336.     TrackCursor;
  3337.     CurrPos.Row := Max(1, LongInt(CurrPos.Row) - TotalRows);
  3338.     SetScreenRowStop(Max(1, Pred(ScreenBlock^.Start.Row)));
  3339.     UpdateScreenBlockDisplay;
  3340.     Desktop^.Unlock;
  3341.   end; {...if CurrPos.Row > 1 }
  3342. end; {...TSpreadSheet.MovePgUp }
  3343.  
  3344.  
  3345. procedure TSpreadSheet.MoveRight;
  3346. { Moves the cursor one column to the right }
  3347. var
  3348.   OldPos : CellPos;
  3349. begin
  3350.   if CurrPos.Col < MaxCols then
  3351.   begin
  3352.     CheckForDragging;
  3353.     Desktop^.Lock;
  3354.     OldPos := CurrPos;
  3355.     if GoToEnd then
  3356.       begin
  3357.         CurrPos.Col := MaxCols;
  3358.         ToggleEnd;
  3359.       end {...if GoToEnd }
  3360.     else
  3361.       Inc(CurrPos.Col);
  3362.     if TrackCursor then
  3363.       UpdateScreenBlockDisplay
  3364.     else
  3365.       MoveCell(OldPos);
  3366.     Desktop^.Unlock;
  3367.   end; {...if CurrPos.Col < MaxCols }
  3368. end; {...TSpreadSheet.MoveRight }
  3369.  
  3370.  
  3371. procedure TSpreadSheet.MoveUp;
  3372. { Moves the cursor one row up }
  3373. var
  3374.   OldPos : CellPos;
  3375. begin
  3376.   if CurrPos.Row > 1 then
  3377.   begin
  3378.     CheckForDragging;
  3379.     Desktop^.Lock;
  3380.     OldPos := CurrPos;
  3381.     if GoToEnd then
  3382.       begin
  3383.         CurrPos.Row := 1;
  3384.         ToggleEnd;
  3385.       end {...if GoToEnd }
  3386.     else
  3387.       Dec(CurrPos.Row);
  3388.     if TrackCursor then
  3389.       UpdateScreenBlockDisplay
  3390.     else
  3391.       MoveCell(OldPos);
  3392.     Desktop^.Unlock;
  3393.   end; {...if CurrPos.Row > 1 }
  3394. end; {...TSpreadSheet.MoveUp }
  3395.  
  3396.  
  3397. function TSpreadSheet.OverwriteHashStart: BucketRange;
  3398. { Returns the initial number of buckest for the OverwriteHash }
  3399. begin
  3400.   OverwriteHashStart := 10;
  3401. end; {...TSpreadSheet.OverwriteHashStart}
  3402.  
  3403.  
  3404. function TSpreadSheet.Parser: PParserObject;
  3405. { Returns a pointer to the parser to be used }
  3406. begin
  3407.   Parser := StandardParser;
  3408. end; {...TSpreadSheet.Parser }
  3409.  
  3410.  
  3411. procedure TSpreadSheet.PasteBlock(DestBlock: TBlock; Formulas: Word);
  3412. { Moves or copies a block of cells to a new position }
  3413. var
  3414.   AnyChanged, Deleted, Good : Boolean;
  3415.   DestPos, SrcPos : CellPos;
  3416.   FormOp : FormulaOps;
  3417.   CellPtr, CP : PCell;
  3418.   ColChange, RowChange : ShortInt;
  3419.   SrcStartCol, DestStartCol : Word;
  3420. const
  3421.   CopyColLitBit = $01;
  3422.   CopyRowLitBit = $02;
  3423. begin
  3424.   Good := True;
  3425.   with ClipBoard do
  3426.   begin
  3427.     if SameCellPos(BlockToCopy^.Start, BlockToCopy^.Stop) then
  3428.       { A single cell will be copied to a block of cells }
  3429.       begin
  3430.         SrcPos := BlockToCopy^.Start;
  3431.         DestPos := DestBlock.Start;
  3432.  
  3433.         if DestBlock.CellInBlock(SrcPos) and
  3434.            (SourceSpreadSheet = @Self) then
  3435.           { if the source cell is in the destination block then
  3436.             delete it from the cell hash to avoid storing the same
  3437.             cell twice at the same position }
  3438.           CellHash.Delete(SrcPos, CellPtr)
  3439.         else
  3440.           CellPtr := SourceCellHash^.Search(SrcPos);
  3441.         if CellPtr <> Empty then
  3442.         begin
  3443.           while Good and (DestPos.Row <= DestBlock.Stop.Row) do
  3444.           begin
  3445.             DestPos.Col := DestBlock.Start.Col;
  3446.             while Good and (DestPos.Col <= DestBlock.Stop.Col) do
  3447.             begin
  3448.               with CellPtr^ do
  3449.               begin
  3450.                 { Delete the current cell in the destination position }
  3451.                 DeleteCell(DestPos, Deleted);
  3452.  
  3453.                 { Add a copy of the source cell in the new position }
  3454.                 Good := AddCell(CellType, DestPos, HasError, CurrValue,
  3455.                   CopyString);
  3456.                 if Good then
  3457.                   AnyChanged := True
  3458.                 else
  3459.                   begin
  3460.                     if DestBlock.CellInBlock(SrcPos) and
  3461.                        (SourceSpreadSheet = @Self) then
  3462.                     { if the cell was not added to the cell hash table
  3463.                       because of a low memory error, and the source cell was
  3464.                       in the destination block, then add the source cell
  3465.                       to the table at the destination position.  This can be
  3466.                       done because the source cell already has memory
  3467.                       allocated and it does not use more memory when added to
  3468.                       the hash table }
  3469.                     begin
  3470.                       CellPtr^.Loc := DestPos;
  3471.                       CellHash.Add(CellPtr)
  3472.                     end; {...if DestBlock.CellInBlock(SrcPos) and... }
  3473.                   end; {...if/else }
  3474.  
  3475.                 { Determine if cell addresses in formulas should be modified }
  3476.                 CP := CellHash.Search(DestPos);
  3477.                 if (CP <> NIL) and CP^.ShouldUpdate then
  3478.                 begin
  3479.                   if (Formulas and CopyColLitBit) = 0 then
  3480.                   { Formula column addresses must be modified }
  3481.                   begin
  3482.                     if DestPos.Col >= SrcPos.Col then
  3483.                       { The column addresses must be increased }
  3484.                       FormOp := opInsert
  3485.                     else
  3486.                       { The column addresses must be decreased }
  3487.                       FormOp := opDelete;
  3488.                     FixFormulaCol(CP, FormOp, 0, Abs(LongInt(DestPos.Col) -
  3489.                       LongInt(SrcPos.Col)), MaxCols, MaxRows);
  3490.                   end; {...if (Formulas and CopyColLitBit) = 0 }
  3491.                   if (Formulas and CopyRowLitBit) = 0 then
  3492.                   { Formula row addresses must be modified }
  3493.                   begin
  3494.                     if DestPos.Row >= SrcPos.Row then
  3495.                       { The row addresses must be increased }
  3496.                       FormOp := opInsert
  3497.                     else
  3498.                       { The row addresses must be decreased }
  3499.                       FormOp := opDelete;
  3500.                     FixFormulaRow(CP, FormOp, 0, Abs(LongInt(DestPos.Row) -
  3501.                       LongInt(SrcPos.Row)), MaxCols, MaxRows);
  3502.                   end; {...if (Formulas and CopyRowLitBit) = 0 }
  3503.                 end; {...if (CP <> NIL) and CP^.ShouldUpdate }
  3504.               end; {...with CellPtr^}
  3505.               Inc(DestPos.Col);
  3506.             end; {...while Good and (DestPos.Col <= DestBlock.Stop.Col) }
  3507.             Inc(DestPos.Row);
  3508.           end; {...while Good and (DestPos.Row <= DestBlock.Stop.Row) }
  3509.  
  3510.           if DestBlock.CellInBlock(SrcPos) and (SourceSpreadSheet = @Self) then
  3511.           { Discard the original cell, since a new copy of it was added in
  3512.             the same position }
  3513.             Dispose(CellPtr, Done)
  3514.           else if (Operation = opMove) and Good then
  3515.           { if the source cell was in the destination block, and it was
  3516.             a move operation, then delete the source cell }
  3517.             SourceSpreadSheet^.DeleteCell(SrcPos, Deleted);
  3518.         end; {...if CellPtr <> Empty }
  3519.       end {...if SameCellPos(BlockToCopy^.Start, BlockToCopy^.Stop) }
  3520.     else
  3521.       begin
  3522.         if not (SameCellPos(BlockToCopy^.Start, DestBlock.Start) and
  3523.            (SourceSpreadSheet = @Self)) then
  3524.         { Continue only after verifying that a block is not going to be
  3525.           copied into itself }
  3526.         begin
  3527.           if (BlockToCopy^.Start.Col < DestBlock.Start.Col) and
  3528.              (SourceSpreadSheet = @Self) then
  3529.             { if the possibility exists that the blocks may overlap in such
  3530.               a way that cells of the source block are overwritten by the
  3531.               cells in the destination block before they are copied, then
  3532.               copy the blocks backwards }
  3533.             begin
  3534.               ColChange := -1;
  3535.               SrcPos.Col := BlockToCopy^.Stop.Col;
  3536.               DestPos.Col := DestBlock.Stop.Col;
  3537.             end {...if (BlockToCopy^.Start.Col < DestBlock.Start.Col) }
  3538.           else
  3539.             begin
  3540.               ColChange := 1;
  3541.               SrcPos.Col := BlockToCopy^.Start.Col;
  3542.               DestPos.Col := DestBlock.Start.Col;
  3543.             end; {...if/else }
  3544.           if (BlockToCopy^.Start.Row < DestBlock.Start.Row) and
  3545.              (SourceSpreadSheet = @Self) then
  3546.             { if the possibility exists that the blocks may overlap in such
  3547.               a way that cells of the source block are overwritten by the
  3548.               cells in the destination block before they are copied, then
  3549.               copy the blocks backwards }
  3550.             begin
  3551.               RowChange := -1;
  3552.               SrcPos.Row := BlockToCopy^.Stop.Row;
  3553.               DestPos.Row := DestBlock.Stop.Row;
  3554.             end {...if (BlockToCopy^.Start.Row < DestBlock.Start.Row) }
  3555.           else
  3556.             begin
  3557.               RowChange := 1;
  3558.               SrcPos.Row := BlockToCopy^.Start.Row;
  3559.               DestPos.Row := DestBlock.Start.Row;
  3560.             end; {...if/else }
  3561.  
  3562.           { Assign values to the SrcStartCol and DestStartCol which indicate
  3563.             the column of the first cell that has to be copied everytime a
  3564.             new row is selected for copying }
  3565.           SrcStartCol := SrcPos.Col;
  3566.           DestStartCol := DestPos.Col;
  3567.  
  3568.           with BlockToCopy^ do
  3569.           begin
  3570.             while Good and ((SrcPos.Row <= Stop.Row) and
  3571.                (SrcPos.Row >= Start.Row)) and (DestPos.Row <= MaxRows) do
  3572.             begin
  3573.               SrcPos.Col := SrcStartCol;
  3574.               DestPos.Col := DestStartCol;
  3575.               while Good and ((SrcPos.Col <= Stop.Col) and
  3576.                 (SrcPos.Col >= Start.Col)) and (DestPos.Col <= MaxCols) do
  3577.               begin
  3578.                 CellPtr := SourceCellHash^.Search(SrcPos);
  3579.                 CellHash.Delete(DestPos, CP);
  3580.                 if CP <> NIL then
  3581.                   Dispose(CP, Done);
  3582.                 if (CellPtr <> Empty) and (CellPtr <> NIL) then
  3583.                 begin
  3584.                   with CellPtr^ do
  3585.                   begin
  3586.                     Good := AddCell(CellType, DestPos, HasError, CurrValue,
  3587.                       CopyString);
  3588.                     if Good then
  3589.                     begin
  3590.                       AnyChanged := True;
  3591.                       CellPtr := CellHash.Search(DestPos);
  3592.                       if CellPtr^.ShouldUpdate then
  3593.                       begin
  3594.                         if (Formulas and CopyColLitBit) = 0 then
  3595.                         begin
  3596.                           if DestPos.Col >= SrcPos.Col then
  3597.                             FormOp := opInsert
  3598.                           else
  3599.                             FormOp := opDelete;
  3600.                           FixFormulaCol(CellPtr,FormOp, 0,
  3601.                             Abs(LongInt(DestPos.Col) - LongInt(SrcPos.Col)),
  3602.                             MaxCols, MaxRows);
  3603.                         end; {...if (Fomulas and CopyColLitBit) = 0 }
  3604.                         if (Formulas and CopyRowLitBit) = 0 then
  3605.                         begin
  3606.                           if DestPos.Row >= SrcPos.Row then
  3607.                             FormOp := opInsert
  3608.                           else
  3609.                             FormOp := opDelete;
  3610.                           FixFormulaRow(CellPtr, FormOp, 0,
  3611.                             Abs(LongInt(DestPos.Row) - LongInt(SrcPos.Row)),
  3612.                             MaxCols, MaxRows);
  3613.                         end; {...if (Formulas and CopyRowLitBit) = 0 }
  3614.                       end; {...if CellPtr^.ShouldUpdate }
  3615.                     end; {...if Good }
  3616.                   end; {...with CellPtr^ }
  3617.                 end; {...if (CellPtr <> Empty) and (CellPtr <> NIL) }
  3618.                 if (Operation = opMove) and Good then
  3619.                 begin
  3620.                   SourceCellHash^.Delete(SrcPos, CP);
  3621.                   if CP <> NIL then
  3622.                     Dispose(CP, Done);
  3623.                 end; {...if (Operation = opMove) and Good }
  3624.                 Inc(DestPos.Col, ColChange);
  3625.                 Inc(SrcPos.Col, ColChange);
  3626.               end; {...while Good and ((SrcPos.Col <= Stop.Col) and ... }
  3627.               Inc(DestPos.Row, RowChange);
  3628.               Inc(SrcPos.Row, RowChange);
  3629.             end; {...while Good and ((SrcPos.Row <= Stop.Row) and ... }
  3630.           end; {...with BlockToCopy^ }
  3631.         end; {...if not SameCellPos(BlockToCopy^.Start, DestBlock.Start) ... }
  3632.       end; {...if/else }
  3633.   end; {...with ClipBoard }
  3634. end; {...TSpreadSheet.PasteBlock }
  3635.  
  3636.  
  3637. procedure TSpreadSheet.PasteCellBlock;
  3638. { Copies a block from the source location to the current position }
  3639. var
  3640.   Dialog : PDialog;
  3641.   Block : TBlock;
  3642. begin
  3643.   with ClipBoard do
  3644.   begin
  3645.     { if the clipboard is active, then continue with the paste operation }
  3646.     if Active then
  3647.     begin
  3648.       { Determine the destination block }
  3649.       if BlockOn then
  3650.         Block.Init(CurrBlock^.Start)
  3651.       else
  3652.         Block.Init(CurrPos);
  3653.       if SameCellPos(BlockToCopy^.Start, BlockToCopy^.Stop) then
  3654.         { if its only one cell that will be copied in a block of cells then
  3655.           the destination block will be the currently selected block (if
  3656.           there is no block selected, the destination block will be the
  3657.           current cell }
  3658.         begin
  3659.           if BlockOn then
  3660.             Block.Stop := CurrBlock^.Stop
  3661.         end {...if SameCellPos(BlockToCopy^.Start, BlockToCopy^.Stop) }
  3662.       else
  3663.         { if a block of cells will be copied, then the destination block will
  3664.           have the same dimensions as the original block of cells }
  3665.         begin
  3666.           Inc(Block.Stop.Col, BlockToCopy^.Stop.Col - BlockToCopy^.Start.Col);
  3667.           Inc(Block.Stop.Row, BlockToCopy^.Stop.Row - BlockToCopy^.Start.Row);
  3668.         end; {...if/else }
  3669.  
  3670.       { Verifies that no protected cells are being deleted or overwritten }
  3671.       if SheetProtected then
  3672.       begin
  3673.         { Verifies that there are no protected cells in the destination
  3674.           block that could be overwritten }
  3675.         if CellsProtected(Block) then
  3676.         begin
  3677.           MessageBox(GLStringList^.Get(sCellsProtectedMsg), NIL,
  3678.             mfInformation + mfOKButton);
  3679.           Exit;
  3680.         end ; {...if CellsProtected(Block) }
  3681.       end; {...if SheetProtected }
  3682.  
  3683.       { Execute the dialog requesting instructions on whether to update or
  3684.         not the formulas (if any) in the block to be copied or moved }
  3685.       Dialog := PDialog(GLResFile^.Get('FormulasDialog'));
  3686.       if Application^.ValidView(Dialog) <> NIL then
  3687.       begin
  3688.         EraseMessage;
  3689.         if Desktop^.ExecView(Dialog) <> cmCancel then
  3690.         begin
  3691.           Dialog^.GetData(RCopyFormulas);
  3692.           Dispose(Dialog, Done);
  3693.           Dialog := PDialog(GLResFile^.Get('UpdatingTablesDialog'));
  3694.           if Application^.ValidView(Dialog) <> NIL then
  3695.             Desktop^.Insert(Dialog)
  3696.           else
  3697.             Exit;
  3698.           PasteBlock(Block, RCopyFormulas.CopyFormulas);
  3699.           Desktop^.Delete(Dialog);
  3700.           if (SourceSpreadSheet <> @Self) and (SourceSpreadSheet <> NIL) then
  3701.             SourceSpreadSheet^.DisplayAllCells;
  3702.           DisplayAllCells;
  3703.           ToggleClipBoardOff;
  3704.         end; {...if Desktop^.ExecView(Dialog) <> cmCancel }
  3705.         Dispose(Dialog, Done);
  3706.       end; {...if Application^.ValidView(Dialog) <> NIL }
  3707.     end; {...if Active }
  3708.   end; {...with ClipBoard }
  3709. end; {...TSpreadSheet.PasteCellBlock }
  3710.  
  3711.  
  3712. procedure TSpreadSheet.Print;
  3713. { Prints the spreadsheet }
  3714. var
  3715.   Dialog : PDialog;
  3716.   Error,                   { Is set to true whenever an error ocurrs }
  3717.   Finished : Boolean;      { Is set to true when the print job is finished }
  3718.   FileString : PathStr;
  3719.   OutputFile : Text;       { File used for output }
  3720.   PageH,                   { Horizontal position of the page being printed }
  3721.   PageV,                   { Vertical position of the page being printed }
  3722.   SelectedCommand,         { Stores the result from the message box dialogs }
  3723.  
  3724.   StartCol,                { Starting column of the page being printed }
  3725.   StartRow : Word;         { Starting row of the page being printed }
  3726.  
  3727.   TopM, BottomM, LeftM,    {    Used to store the     }
  3728.   RightM, PageR, PageCols, {     values entered       }
  3729.   ColsN, ColsC : Byte;     {    in the Print Dialog   }
  3730.  
  3731.   Code : Integer;          { Return code of the val function }
  3732.  
  3733.  
  3734.     function CheckForEscape: Boolean;
  3735.     { Checks the event buffer to see if ESC has been pressed to
  3736.     cancel the print job }
  3737.     var
  3738.       Event : TEvent;
  3739.     begin
  3740.       CheckForEscape := False;
  3741.       GetEvent(Event);
  3742.       if Event.What = evKeyDown then
  3743.         if Event.KeyCode = kbEsc then
  3744.         begin
  3745.           { if ESC was pressed, delete the 'Printing...' dialog
  3746.             and prompt the user for confirmation }
  3747.           Desktop^.Delete(Dialog);
  3748.           if MessageBox(GLStringList^.Get(sCancelPrintJob), NIL,
  3749.              mfError + mfYesButton + mfNoButton) = cmYes then
  3750.             CheckForEscape := True
  3751.           else
  3752.             Desktop^.Insert(Dialog);
  3753.         end {...if Event.KeyCode = kbEsc }
  3754.     end; {...CheckForEscape }
  3755.  
  3756.     function PrintChar(C: String): Boolean;
  3757.     { Prints a code to the assigned device without a sending a CR }
  3758.     begin
  3759.       PrintChar := True;
  3760.       repeat
  3761.         if CheckForEscape then
  3762.         begin
  3763.           PrintChar := False;
  3764.           Exit;
  3765.         end; {...if CheckForEscape }
  3766.         Error := False;
  3767.         {$I-}
  3768.         Write(OutputFile, C);
  3769.         {$I+}
  3770.         if IOResult <> 0 then
  3771.         begin
  3772.           Error := True;
  3773.           if FileString = DefaultPrinterName then
  3774.             begin
  3775.               Desktop^.Delete(Dialog);
  3776.               SelectedCommand := MessageBox(
  3777.                 GLStringList^.Get(sPrinterPrintErrorMsg), NIL, mfError +
  3778.                 mfYesButton + mfNoButton);
  3779.               if SelectedCommand = cmNo then
  3780.                 PrintChar := False
  3781.               else
  3782.                 { Since the print job will continue, display again
  3783.                   the 'Printing...' dialog }
  3784.                 Desktop^.Insert(Dialog);
  3785.             end {...if FileString = DefaultPrinterName }
  3786.           else
  3787.             begin
  3788.               SelectedCommand := MessageBox(
  3789.                 GLStringList^.Get(sFilePrintErrorMsg), NIL, mfError +
  3790.                 mfYesButton + mfNoButton);
  3791.               if SelectedCommand = cmNo then
  3792.                 PrintChar := False
  3793.               else
  3794.                 Desktop^.Insert(Dialog);
  3795.             end; {...if/else }
  3796.         end; {...if IOResult <> 0 }
  3797.       until not Error or (SelectedCommand = cmNo);
  3798.     end; {...PrintChar }
  3799.  
  3800.     function PrintString(S: String): Boolean;
  3801.     { Prints a string to the assigned device }
  3802.     begin
  3803.       PrintString := True;
  3804.       repeat
  3805.         if CheckForEscape then
  3806.         begin
  3807.           PrintString := False;
  3808.           Exit;
  3809.         end; {...if CheckForEscape }
  3810.         Error := False;
  3811.         {$I-}
  3812.         Writeln(OutputFile, S);
  3813.         {$I+}
  3814.         if IOResult <> 0 then
  3815.         begin
  3816.           Error := True;
  3817.           if FileString = DefaultPrinterName then
  3818.             begin
  3819.               SelectedCommand := MessageBox(
  3820.                 GLStringList^.Get(sPrinterPrintErrorMsg), NIL, mfError +
  3821.                 mfYesButton + mfNoButton);
  3822.               if SelectedCommand = cmNo then
  3823.                 PrintString := False
  3824.               else
  3825.                 Desktop^.Insert(Dialog);
  3826.             end {...if FileString = DefaultPrinterName }
  3827.           else
  3828.             begin
  3829.               SelectedCommand := MessageBox(
  3830.                 GLStringList^.Get(sFilePrintErrorMsg), NIL, mfError +
  3831.                 mfYesButton + mfNoButton);
  3832.               if SelectedCommand = cmNo then
  3833.                 PrintString := False
  3834.               else
  3835.                 Desktop^.Insert(Dialog);
  3836.             end; {...if/else }
  3837.         end; {...if IOResult <> 0}
  3838.       until not Error or (SelectedCommand = cmNo);
  3839.     end; {...PrintString }
  3840.  
  3841.     function RowStartString(Row: Word): String;
  3842.     { Returns the row string to be printed at the beginning of a line }
  3843.     begin
  3844.       RowStartString := '';
  3845.       with RPrint do
  3846.       begin
  3847.         if PrintRows <> 0 then
  3848.         begin
  3849.           if ((PrintRows = 1) and (PageH = 1)) or (PrintRows = 2) then
  3850.           begin
  3851.             RowStartString := LeftJustStr(RowToString(Row), RowNumberSpace);
  3852.             RowStartString[RowNumberSpace] := '│';
  3853.           end; {...if ((PrintRows = 1) and (PageH = 1)) or... }
  3854.         end; {...if PrintRows <> 0 }
  3855.       end; {...with RPrint }
  3856.     end; {...RowStartString }
  3857.  
  3858.     function PrintPage: Boolean;
  3859.     { Prints one page of the spreadsheet }
  3860.     var
  3861.       Color : Byte; { Simply used to fill the list of parameters for
  3862.                      the CellToFString method }
  3863.       Cols, Counter, Rows : Byte;
  3864.       Pos : CellPos;
  3865.       S : String;
  3866.     const
  3867.       OutlineBit = $01;
  3868.       BoldBit = $02;
  3869.     begin
  3870.       PrintPage := False;
  3871.       with RPrint, PrinterConfigRec do
  3872.       begin
  3873.  
  3874.         { Top margin }
  3875.         for Counter := 1 to TopM do
  3876.           if not PrintString('') then
  3877.             Exit;
  3878.  
  3879.         { Determine the number of rows that will fit in the page }
  3880.         Rows := Min((PageR - TopM - BottomM), Succ(MaxRows - StartRow));
  3881.  
  3882.         { One row will be used if the column headers will be printed }
  3883.         if PrintColumns in [1,2] then
  3884.           Dec(Rows);
  3885.  
  3886.         { Determine the number of columns that can fit in a page }
  3887.         Cols := 0;
  3888.         Counter := Length(RowStartString(StartRow));
  3889.         while Counter <= PageCols do
  3890.         begin
  3891.           Inc(Counter, ColWidth(Cols + StartCol));
  3892.           Inc(Cols);
  3893.         end; {...while Counter <= PageCols }
  3894.         Dec(Cols);
  3895.         Cols := Min(Cols, Succ(MaxCols - StartCol));
  3896.  
  3897.         if ((PrintColumns = 1) and (PageV = 1)) or (PrintColumns = 2) then
  3898.         { Print the column headers if requested }
  3899.         begin
  3900.           S := FillString(Length(RowStartString(StartRow)), ' ');
  3901.           for Counter := StartCol to Pred(StartCol + Cols) do
  3902.             S := S + CenterStr(ColumnToString(Counter), ColWidth(Counter));
  3903.           if not PrintChar(PrinterUnderlineOnCode) then
  3904.             Exit;
  3905.           if (Other and BoldBit) <> 0 then
  3906.             if not PrintChar(PrinterBoldOnCode) then
  3907.               Exit;
  3908.           if not PrintString(S) then
  3909.             Exit;
  3910.           if (Other and BoldBit) <> 0 then
  3911.             if not PrintChar(PrinterBoldOffCode) then
  3912.               Exit;
  3913.           if not PrintChar(PrinterUnderlineOffCode) then
  3914.             Exit;
  3915.         end; {...if ((PrintColumns = 1) and (PageV = 1))... }
  3916.  
  3917.         { Print the data }
  3918.         for Pos.Row := StartRow to Pred(StartRow + Rows) do
  3919.         begin
  3920.           S := RowStartString(Pos.Row);
  3921.           if S <> '' then
  3922.           { Print the row numbers }
  3923.           begin
  3924.             if (Other and BoldBit) <> 0 then
  3925.               if not PrintChar(PrinterBoldOnCode) then
  3926.                 Exit;
  3927.             if not PrintChar(S) then
  3928.               Exit;
  3929.             if (Other and BoldBit) <> 0 then
  3930.               if not PrintChar(PrinterBoldOffCode) then
  3931.                 Exit;
  3932.             S := '';
  3933.           end; {...if S <> '' }
  3934.           for Pos.Col := StartCol to Pred(StartCol + Cols) do
  3935.             S := S + CellToFString(Pos, Color);
  3936.           if not PrintString(S) then
  3937.             Exit;
  3938.         end; {...for Pos.Row }
  3939.  
  3940.         Inc(StartCol, Cols);
  3941.         if (StartCol > LastPos.Col) or (StartCol = 0) then
  3942.           begin
  3943.             Inc(StartRow, Rows);
  3944.             if (StartRow > LastPos.Row) or (StartRow = 0) then
  3945.               Finished := True
  3946.             else
  3947.               begin
  3948.                 Inc(PageV);
  3949.                 PageH := 1;
  3950.                 StartCol := 1;
  3951.               end; {...if/else }
  3952.           end {...if (StartCol > LastPos.Col) or (StartCol = 0) }
  3953.         else
  3954.           Inc(PageH);
  3955.         if not PrintChar(Chr(FF)) then
  3956.           Exit;
  3957.       end; {...with RPrint, PrinterConfigRec }
  3958.       PrintPage := True;
  3959.     end; {...PrintPage }
  3960.  
  3961.     procedure EndPrintJob;
  3962.     { Does all the necessary clean up when finishing a print job }
  3963.     begin
  3964.       Close(OutputFile);
  3965.       InitSysError;
  3966.     end; {...EndPrintJob }
  3967.  
  3968. begin
  3969.   Dialog := PDialog(GLResFile^.Get('PrintDialog'));
  3970.   Dialog^.SetData(RPrint);
  3971.   if Application^.ValidView(Dialog) <> NIL then
  3972.     begin
  3973.       if Desktop^.ExecView(Dialog) <> cmCancel then
  3974.         Dialog^.GetData(RPrint)
  3975.       else
  3976.         begin
  3977.           Dispose(Dialog, Done);
  3978.           Exit;
  3979.         end; {...if/else }
  3980.     end {...if Application^.ValidView(Dialog) <> NIL }
  3981.   else
  3982.     Exit;
  3983.   Dispose(Dialog, Done);
  3984.   with RPrint, PrinterConfigRec do
  3985.   begin
  3986.     if PrintTo = 0 then
  3987.       FileString := DefaultPrinterName
  3988.     else
  3989.       begin
  3990.         Dialog := PFileDialog(GLResFile^.Get('PrintToDialog'));
  3991.         if Application^.ValidView(Dialog) <> NIL then
  3992.           begin
  3993.             if Desktop^.ExecView(Dialog) <> cmCancel then
  3994.               Dialog^.GetData(FileString)
  3995.             else
  3996.               begin
  3997.                 Dispose(Dialog, Done);
  3998.                 Exit;
  3999.               end; {...if/else }
  4000.           end {...if Application^.ValidView(Dialog) <> NIL }
  4001.         else
  4002.           Exit;
  4003.         Dispose(Dialog, Done);
  4004.       end; {...if/else }
  4005.  
  4006.     { Disables Turbo Vision's system error handler to be able to handle
  4007.       print errors differently }
  4008.     DoneSysError;
  4009.  
  4010.     repeat
  4011.       Error := False;
  4012.       {$I-}
  4013.       Assign(OutputFile, FileString);
  4014.       Rewrite(OutputFile);
  4015.       {$I+}
  4016.       if IOResult <> 0 then
  4017.       { if the file could not be opened, prompt the user wether to
  4018.         continue with or cancel the print job }
  4019.       begin
  4020.         Error := True;
  4021.         SelectedCommand := MessageBox(GLStringList^.Get(sPrintInitErrorMsg),
  4022.           NIL, mfYesButton + mfNoButton);
  4023.         if SelectedCommand = cmNo then
  4024.         begin
  4025.           EndPrintJob;
  4026.           Exit;
  4027.         end; {...if SelectedCommand = cmNo }
  4028.       end; {...if IOResult <> 0 }
  4029.     until not Error;
  4030.  
  4031.     { Convert to numbers the values entered in the 'Print Dialog' }
  4032.     val(TopMargin, TopM, Code);
  4033.     val(BottomMargin, BottomM, Code);
  4034.     val(LeftMargin, LeftM, Code);
  4035.     val(RightMargin, RightM, Code);
  4036.     val(PageRows, PageR, Code);
  4037.     val(NormalCols, ColsN, Code);
  4038.     val(CondensedCols, ColsC, Code);
  4039.  
  4040.     { Determine the number of columns available for printing }
  4041.     if PrintSize = 1 then
  4042.       begin
  4043.         if not PrintChar(PrinterCondensedOnCode) then
  4044.         begin
  4045.           EndPrintJob;
  4046.           Exit;
  4047.         end; {...if not PrintChar(PrinterCondensedOnCode) }
  4048.         PageCols := ColsC;
  4049.       end {...if PrintSize = 1}
  4050.     else
  4051.       PageCols := ColsN;
  4052.     PageV := 1;
  4053.     PageH := 1;
  4054.     StartCol := 1;
  4055.     StartRow := 1;
  4056.     Finished := False;
  4057.  
  4058.     { Display a dialog to indicate the file is being printed }
  4059.     Dialog := PDialog(GLResFile^.Get('PrintingDialog'));
  4060.     if Application^.ValidView(Dialog) <> NIL then
  4061.       Desktop^.Insert(Dialog)
  4062.     else
  4063.       begin
  4064.         if Dialog <> NIL then
  4065.           Dispose(Dialog, Done);
  4066.         EndPrintJob;
  4067.         Exit;
  4068.       end; {...if/else }
  4069.     repeat
  4070.       if not PrintPage then
  4071.       begin
  4072.         EndPrintJob;
  4073.         { It is not necessary to delete the dialog from the desktop
  4074.           since the dialog is deleted before prompting the user
  4075.           for cancelation }
  4076.         Dispose(Dialog, Done);
  4077.         Exit;
  4078.       end; {...if not PrintPage }
  4079.     until Finished;
  4080.     if not PrintChar(PrinterCondensedOffCode) or
  4081.        not PrintChar(PrinterUnderlineOffCode) then
  4082.     begin
  4083.       EndPrintJob;
  4084.       Desktop^.Delete(Dialog);
  4085.       Dispose(Dialog, Done);
  4086.       Exit;
  4087.     end; {...if not PrintChar(PrinterCondensedOffCode) or ... }
  4088.     EndPrintJob;
  4089.     Desktop^.Delete(Dialog);
  4090.     Dispose(Dialog, Done);
  4091.   end; {...with RPrint, PrinterConfigRec }
  4092. end; {...TSpreadSheet.Print }
  4093.  
  4094.  
  4095. procedure TSpreadSheet.Recalc(Display: Boolean);
  4096. { Recalculates all the values that need to be recalculated }
  4097. var
  4098.   Pos : CellPos;
  4099.  
  4100.     procedure DoUpdate;
  4101.     var
  4102.       NewPos : CellPos;
  4103.       CellPtr : PCell;
  4104.       CellsOverWritten : Word;
  4105.       FormulaStr : PString;
  4106.     begin
  4107.       with CellHash do
  4108.       begin
  4109.         CellPtr := Search(Pos);
  4110.         if CellPtr^.ShouldUpdate then
  4111.         begin
  4112.           with PFormulaCell(CellPtr)^ do
  4113.           begin
  4114.             FormulaStr := NewStr(Formula.ToString);
  4115.             Parser^.Init(@CellHash, FormulaStr, MaxCols, MaxRows);
  4116.             Parser^.Parse;
  4117.             DisposeStr(FormulaStr);
  4118.             Value := Parser^.ParseValue;
  4119.             Error := Parser^.ParseError;
  4120.             CellsOverWritten := CellPtr^.OverWritten(CellHash, FormatHash,
  4121.               WidthHash, LastPos, MaxCols, GetColWidth, DisplayFormulas);
  4122.             if OverWriteHash.Change(CellPtr, CellsOverWritten) and Display and
  4123.                (CellPtr^.Loc.Col + CellsOverWritten >=
  4124.                ScreenBlock^.Start.Col) then
  4125.             begin
  4126.               NewPos := CellPtr^.Loc;
  4127.               for NewPos.Col := CellPtr^.Loc.Col to ScreenBlock^.Stop.Col do
  4128.               begin
  4129.                 if ScreenBlock^.CellInBlock(NewPos) then
  4130.                   DisplayCell(NewPos);
  4131.               end; {...for NewPos.Col }
  4132.             end; {...if OverWriteHash.Change(CellPtr, CellsOverWritten) ... }
  4133.           end; {...with PFormulaCell(CellPtr)^ }
  4134.         end; {...if CellPtr^.ShouldUpdate }
  4135.       end; {...with CellHash }
  4136.     end; {...DoUpdate }
  4137.  
  4138. begin
  4139.   DisplayMessage(GLStringList^.Get(sRecalcMsg));
  4140.   for Pos.Row := 1 to LastPos.Row do
  4141.     for Pos.Col := 1 to LastPos.Col do
  4142.       DoUpdate;
  4143.   for Pos.Row := LastPos.Row downto 1 do
  4144.     for Pos.Col := LastPos.Col downto 1 do
  4145.       DoUpdate;
  4146.   EraseMessage;
  4147. end; {...TSpreadSheet.Recalc }
  4148.  
  4149.  
  4150. function TSpreadsheet.RowToY(Row : Integer) : Byte;
  4151. { Returns the screen position of a particular row }
  4152. begin
  4153.   RowToY := (Row - ScreenBlock^.Start.Row) + DisplayArea.UpperLeft.Row ;
  4154. end; {...TSpreadSheet.RowToY }
  4155.  
  4156.  
  4157. function TSpreadSheet.SameCellPos(P1, P2 : CellPos) : Boolean;
  4158. { Returns true if two positions are the same }
  4159. begin
  4160.   SameCellPos := Compare(P1, P2, SizeOf(CellPos));
  4161. end; {...TSpreadSheet.SameCellPos }
  4162.  
  4163.  
  4164. function TSpreadSheet.SelectColumn(var Event: TEvent): Boolean;
  4165. { Marks a complete column as selected }
  4166. var
  4167.   Pos : CellPos;
  4168.   SelectedCol : Integer;
  4169.   Block : TBlock;
  4170.   Mouse : TPoint;
  4171. begin
  4172.    MakeLocal(Event.Where, Mouse);
  4173.    if ColArea.PointInArea(Mouse.X, Mouse.Y) then
  4174.      begin
  4175.        ClearCurrBlock;
  4176.        SelectedCol := XToCol(Mouse.X);
  4177.        if SelectedCol = 0 then
  4178.          Exit;
  4179.        Pos := CurrPos;
  4180.        CurrPos.Row := 1;
  4181.        CurrPos.Col := SelectedCol;
  4182.        ToggleBlockOn;
  4183.        CurrPos.Row := ScreenBlock^.Start.Row;
  4184.        if ScreenBlock^.CellInBlock(Pos) then
  4185.          MoveCell(Pos);
  4186.        Pos.Row := MaxRows;
  4187.        Pos.Col := SelectedCol;
  4188.        CurrBlock^.Stop := Pos;
  4189.        Block.Start := CurrBlock^.Start;
  4190.        Pos.Row := ScreenBlock^.Stop.Row;
  4191.        Block.Stop := Pos;
  4192.        DisplayBlock(Block);
  4193.        DisplayCellData;
  4194.        ClearEvent(Event);
  4195.        SelectColumn := True;
  4196.      end {...if ColArea.PointInArea(Mouse.X, Mouse.Y) }
  4197.    else
  4198.      SelectColumn := False;
  4199. end; {...TSpreadSheet.SelectColumn }
  4200.  
  4201.  
  4202. procedure TSpreadSheet.ScrollDraw;
  4203. { Redraws the spreadsheet whenever the scrollbar changes }
  4204. var
  4205.   Redraw : Boolean;
  4206.   D : TPoint;
  4207. begin
  4208.   Desktop^.Lock;
  4209.   if HScrollBar <> NIL then
  4210.     D.X := HScrollBar^.Value
  4211.   else
  4212.     D.X := 0;
  4213.   if VScrollBar <> NIL then
  4214.     D.Y := VScrollBar^.Value
  4215.   else
  4216.     D.Y := 0;
  4217.   if D.X <> Delta.X then
  4218.   begin
  4219.     with PlimScrollBar(HScrollBar)^, ScreenBlock^ do
  4220.     begin
  4221.       if (Abs(Change) = 1) and not KeyPressed then
  4222.         begin
  4223.           if Abs(Change) = Change then
  4224.             begin
  4225.               if Stop.Col < MaxCols then
  4226.                 begin
  4227.                   Inc(Stop.Col);
  4228.                   SetScreenColStop(Stop.Col);
  4229.                   Redraw := True;
  4230.                 end {...if Stop.Col < MaxCols }
  4231.               else
  4232.                 SetValue(Delta.X);
  4233.             end {...if Abs(Change) = Change }
  4234.           else
  4235.             begin
  4236.               if Start.Col > 1 then
  4237.                 begin
  4238.                   Dec(Start.Col);
  4239.                   SetScreenColStart(Start.Col);
  4240.                   Redraw := True;
  4241.                 end {...if Start.Col > 1 }
  4242.               else
  4243.                 SetValue(Delta.X);
  4244.             end; {...if/else }
  4245.           if Redraw then
  4246.           begin
  4247.             SetBlankArea;
  4248.             DisplayCols;
  4249.             DisplayAllCells;
  4250.             DisplayCellData;
  4251.             if not NoBlankArea then
  4252.               ClearScreenArea(@BlankArea);
  4253.             if Value <> Start.Col then
  4254.             begin
  4255.               Value := Start.Col;
  4256.               HScrollBar^.DrawView;
  4257.             end; {...if Value <> Start.Col }
  4258.             Delta.X := Value;
  4259.           end; {...if Redraw }
  4260.         end {...if (Abs(Change) = 1) and not KeyPressed }
  4261.       else if (Abs(Change) = PgStep) and not KeyPressed then
  4262.         begin
  4263.           if Abs(Change) = Change then
  4264.             begin
  4265.               if Stop.Col < MaxCols then
  4266.                 begin
  4267.                   Start.Col := Succ(Stop.Col);
  4268.                   SetScreenColStart(Start.Col);
  4269.                   Redraw := True;
  4270.                 end {...if Stop.Col < MaxCols }
  4271.               else
  4272.                 SetValue(Delta.X);
  4273.             end {...if Abs(Change) = Change }
  4274.           else
  4275.             begin
  4276.               if Start.Col > 1 then
  4277.                 begin
  4278.                   Stop.Col := Pred(Start.Col);
  4279.                   SetScreenColStop(Stop.Col);
  4280.                   Redraw := True;
  4281.                 end {...if Start.Col > 1 }
  4282.               else
  4283.                 SetValue(Delta.X);
  4284.             end; {...if/else }
  4285.           if Redraw then
  4286.           begin
  4287.             SetBlankArea;
  4288.             DisplayCols;
  4289.             DisplayAllCells;
  4290.             DisplayCellData;
  4291.             if not NoBlankArea then
  4292.               ClearScreenArea(@BlankArea);
  4293.             if Value <> Start.Col then
  4294.               begin
  4295.                 Value := Start.Col;
  4296.                 HScrollBar^.DrawView;
  4297.               end; {...if Value <> Start.Col }
  4298.             Delta.X := Value;
  4299.           end; {...if Redraw }
  4300.         end {...else if (Abs(Change) = PgStep) and not KeyPressed }
  4301.       else
  4302.         begin
  4303.           if (Value <= MaxCols) and (Value >= 1) then
  4304.             begin
  4305.               Start.Col := Value;
  4306.               if KeyPressed then
  4307.                 ExtendCurrBlock(RedrawNo);
  4308.               SetScreenColStart(Start.Col);
  4309.               SetBlankArea;
  4310.               DisplayCols;
  4311.               DisplayAllCells;
  4312.               DisplayCellData;
  4313.               if not NoBlankArea then
  4314.                 ClearScreenArea(@BlankArea);
  4315.               Delta.X := Value;
  4316.             end {...if (Value <= MaxCols) and (Value >= 1) }
  4317.           else
  4318.             SetValue(Delta.X);
  4319.         end; {...if/else }
  4320.     end; {...with PLimScrollBar(HScrollBar^), ScreenBlock^ }
  4321.   end; {...if D.X <> Delta.X }
  4322.   if D.Y <> Delta.Y then
  4323.   begin
  4324.     with PLimScrollBar(VScrollBar)^, ScreenBlock^ do
  4325.     begin
  4326.       if (Abs(Change) = 1) and not KeyPressed then
  4327.         begin
  4328.           if Abs(Change) = Change then
  4329.             begin
  4330.               if Stop.Row < MaxCols then
  4331.                 begin
  4332.                   Inc(Stop.Row);
  4333.                   SetScreenRowStop(Stop.Row);
  4334.                   Redraw := True;
  4335.                 end {...if Stop.Row < MaxCols }
  4336.               else
  4337.                 SetValue(Delta.Y);
  4338.             end {...if Abs(Change) = Change }
  4339.           else
  4340.             begin
  4341.               if Start.Row > 1 then
  4342.                 begin
  4343.                   Dec(Start.Row);
  4344.                   SetScreenRowStart(Start.Row);
  4345.                   Redraw := True;
  4346.                 end {...if Start.Row > 1 }
  4347.               else
  4348.                 SetValue(Delta.Y);
  4349.             end; {...if/else }
  4350.           if Redraw then
  4351.           begin
  4352.             DisplayRows;
  4353.             DisplayAllCells;
  4354.             DisplayCellData;
  4355.             if Value <> Start.Row then
  4356.             begin
  4357.               Value := Start.Row;
  4358.               VScrollBar^.DrawView;
  4359.             end; {...if Value <> Start.Row }
  4360.             Delta.Y := Value;
  4361.           end; {...if Redraw }
  4362.         end {...if (Abs(Change) = 1) and not KeyPressed }
  4363.       else if (Abs(Change) = PgStep) and not KeyPressed then
  4364.         begin
  4365.           if Abs(Change) = Change then
  4366.             begin
  4367.               if Stop.Row < MaxRows then
  4368.                 begin
  4369.                   Start.Row := Start.Row + TotalRows;
  4370.                   if Start.Row > MaxRows then
  4371.                     Start.Row := MaxRows;
  4372.                   SetScreenRowStart(Start.Row);
  4373.                   Redraw := True;
  4374.                 end {...if Stop.Row < MaxRows }
  4375.               else
  4376.                 SetValue(Delta.Y);
  4377.            end {...if Abs(Change) = Change }
  4378.          else
  4379.            begin
  4380.              if Start.Row > 1 then
  4381.                begin
  4382.                  Start.Row := Start.Row - TotalRows;
  4383.                  if Start.Row < 1 then
  4384.                    Start.Row := 1;
  4385.                  SetScreenRowStart(Start.Row);
  4386.                  Redraw := True;
  4387.                end {...if Start.Row > 1 }
  4388.              else
  4389.                SetValue(Delta.Y);
  4390.            end; {...if/else }
  4391.          if Redraw then
  4392.          begin
  4393.            DisplayRows;
  4394.            DisplayAllCells;
  4395.            DisplayCellData;
  4396.            if Value <> Start.Row then
  4397.            begin
  4398.              Value := Start.Row;
  4399.              VScrollBar^.DrawView;
  4400.            end; {...if Value <> Start.Row }
  4401.            Delta.Y := Value;
  4402.          end; {...if Redraw }
  4403.        end {...else if (Abs(Change) = PgStep) and not KeyPressed }
  4404.      else
  4405.        begin
  4406.          if (Value <= MaxRows) and (Value >= 1) then
  4407.            begin
  4408.              Start.Row := Value;
  4409.              if KeyPressed then
  4410.                ExtendCurrBlock(RedrawNo);
  4411.              SetScreenRowStart(Start.Row);
  4412.              DisplayRows;
  4413.              DisplayAllCells;
  4414.              DisplayCellData;
  4415.              Delta.Y := Value;
  4416.            end {...if (Value <= MaxRows) and (Value >= 1) }
  4417.          else
  4418.            SetValue(Delta.Y);
  4419.        end; {...if/else }
  4420.     end; {...with PLimScrollBar(VScrollBar)^, ScreenBlock^ }
  4421.   end; {...if D.Y <> Delta.Y }
  4422.   Desktop^.Unlock;
  4423. end; {...TSpreadSheet.ScrollDraw }
  4424.  
  4425.  
  4426. procedure TSpreadSheet.SetAreas(ScrollArea:TRect);
  4427. { Sets the locations of the different areas of the spreadsheet }
  4428. var
  4429.   x1, x2, y1, y2 : Byte;
  4430. begin
  4431.   x1 := ScrollArea.A.X;
  4432.   y1 := ScrollArea.A.Y;
  4433.   x2 := Pred(ScrollArea.B.X);
  4434.   y2 := Pred(ScrollArea.B.Y);
  4435.   TotalRows := Pred(y2 - Succ(y1));
  4436.   ColArea.Init(x1 + RowNumberSpace, y1, x2, y1, GetColor(6));
  4437.   RowArea.Init(x1, Succ(Y1), Pred(x1 + RowNumberSpace), Pred(Pred(y2)),
  4438.     GetColor(7));
  4439.   InfoArea.Init(x1, y1, Pred(x1 + RowNumberSpace), y1, GetColor(10));
  4440.   DisplayArea.Init(x1 + RowNumberSpace, Succ(y1), x2, Pred(Pred(y2)),
  4441.     GetColor(1));
  4442.   DataArea.Init (x1, Pred(y2), x2, Pred(y2), GetColor(1));
  4443.   ContentsArea.Init (x1, y2, x2, y2, GetColor(9));
  4444.   SetScreenColStart(ScreenBlock^.Start.Col);
  4445.   SetScreenRowStart(ScreenBlock^.Start.Row);
  4446.   SetBlankArea;
  4447. end; {...TSpreadSheet.SetAreas }
  4448.  
  4449.  
  4450. procedure TSpreadSheet.SetBlankArea;
  4451. { Determines if there is a blank area and its location }
  4452. var
  4453.   C : Integer;
  4454. begin
  4455.   Move(DisplayArea, BlankArea, SizeOf(DisplayArea));
  4456.   with BlankArea do
  4457.   begin
  4458.     with ScreenBlock^ do
  4459.       C := ColStart^[Stop.Col - Start.Col] + ColWidth(Stop.Col);
  4460.     if C > DisplayArea.LowerRight.Col then
  4461.       NoBlankArea := True
  4462.     else
  4463.       begin
  4464.         NoBlankArea := False;
  4465.         UpperLeft.Col := C;
  4466.       end; {...if/else }
  4467.   end; {...with BlankArea }
  4468. end; {...TSpreadSheet.SetBlankArea }
  4469.  
  4470.  
  4471.  
  4472. procedure TSpreadSheet.SetChanged(IsChanged: Boolean);
  4473. { Changes the Modified state of the spreadsheet }
  4474. begin
  4475.   Modified := IsChanged;
  4476. end; {...TSpreadSheet.SetChanged }
  4477.  
  4478.  
  4479. procedure TSpreadSheet.SetLimit(X, Y: Integer);
  4480. { Sets the limits of the spreadsheet and adjusts the scrollbars accordingly }
  4481. var
  4482.   R : TRect;
  4483. begin
  4484.   Limit.X := X;
  4485.   Limit.Y := Y;
  4486.   with HScrollBar^ do
  4487.     SetParams (Value, 1, X, Succ(ScreenBlock^.Stop.Col -
  4488.       ScreenBlock^.Start.Col), 1);
  4489.   with VScrollBar^ do
  4490.     SetParams (Value, 1, Y, TotalRows, 1);
  4491. end; {...TSpreadSheet.SetLimit }
  4492.  
  4493.  
  4494. procedure TSpreadSheet.SetLocked;
  4495. { Restores the cells to the locked state, preventing the modification of the
  4496.   cells' contents when the sheet is protected }
  4497. begin
  4498.   if BlockOn then
  4499.     UnlockedHash.Delete(CurrBlock^.Start, CurrBlock^.Stop)
  4500.   else
  4501.     UnlockedHash.Delete(CurrPos, CurrPos);
  4502.   DisplayCellData;
  4503. end; {...TSpreadSheet.SetLocked }
  4504.  
  4505.  
  4506. procedure TSpreadSheet.SetNameWithMouse(var Event: TEvent);
  4507. { Checks to see if the mouse was DoubleClicked in the col area, and if so,
  4508.   it calls the ChangeColNames method }
  4509. var
  4510.   Mouse : TPoint;
  4511.   RealCurrPosCol : Word;
  4512.   SelectedCol : Word;
  4513. begin
  4514.   MakeLocal(Event.Where, Mouse);
  4515.   if ColArea.PointInArea(Mouse.X, Mouse.Y) then
  4516.   begin
  4517.     RealCurrPosCol := CurrPos.Col;
  4518.     SelectedCol := XToCol(Mouse.X);
  4519.     if SelectedCol = 0 then
  4520.       Exit
  4521.     else
  4522.       CurrPos.Col := SelectedCol;
  4523.     ChangeColHeaders;
  4524.     CurrPos.Col := RealCurrPosCol;
  4525.     ClearEvent(Event);
  4526.   end; {...if ColArea.PointInArea(Mouse.X, Mouse.Y) }
  4527. end; {...TSpreadSheet.SetNameWithMouse }
  4528.  
  4529.  
  4530. procedure TSpreadSheet.SetNumber(ANumber: Byte);
  4531. { Sets the spreadsheet number }
  4532. begin
  4533.   Number := ANumber;
  4534. end; {..TSpreadSheet.SetNumber }
  4535.  
  4536.  
  4537. procedure TSpreadSheet.SetProtection(Enable, Display: Boolean);
  4538. { Protects or unprotects the sheet from unauthorized changes }
  4539. begin
  4540.   if Enable then
  4541.     begin
  4542.       SheetProtected := True;
  4543.       DisableCommands([cmChangeColHeaders, cmDeleteColHeaders, cmToggleHeaders,
  4544.         cmToggleFormulas, cmChangeColWidth, cmDeleteColumns, cmInsertColumns,
  4545.         cmDeleteRows, cmInsertRows, cmFormatCells, cmSetUnlocked, cmSetLocked,
  4546.         cmSortData])
  4547.     end {...if Enable }
  4548.   else
  4549.     begin
  4550.       SheetProtected := False;
  4551.       EnableCommands([cmChangeColHeaders, cmDeleteColHeaders, cmToggleHeaders,
  4552.         cmToggleFormulas, cmChangeColWidth, cmDeleteColumns, cmInsertColumns,
  4553.         cmDeleteRows, cmInsertRows, cmFormatCells, cmSetUnlocked, cmSetLocked,
  4554.         cmSortData])
  4555.     end; {...if/else }
  4556.   if Display then
  4557.   begin
  4558.     DisplayAllCells;
  4559.     DisplayCellData;
  4560.   end; {...if Display }
  4561. end; {...TSpreadSheet.SetProtection }
  4562.  
  4563.  
  4564. procedure TSpreadSheet.SetScreenColStart(NewCol:Integer);
  4565. { Determines the starting and ending columns when the starting column is known }
  4566. begin
  4567.   ScreenBlock^.Start.Col := NewCol;
  4568.   FindScreenColStop;
  4569.   FindScreenColStart;
  4570. end; {...TSpreadSheet.SetScreenColStart }
  4571.  
  4572.  
  4573. procedure TSpreadSheet.SetScreenColStop(NewCol:Integer);
  4574. { Determines the starting and ending columns when the ending column is known }
  4575. begin
  4576.   ScreenBlock^.Stop.Col := NewCol;
  4577.   FindScreenColStart;
  4578.   FindScreenColStop;
  4579. end; {...TSpreadSheet.SetScreenColStop }
  4580.  
  4581.  
  4582. procedure TSpreadSheet.SetScreenRowStart(NewRow:Integer);
  4583. { Determines the starting and ending rows when the starting row is known }
  4584. begin
  4585.   ScreenBlock^.Start.Row := NewRow;
  4586.   FindScreenRowStop;
  4587. end; {...TSpreadSheet.SetScreenRowStart }
  4588.  
  4589.  
  4590. procedure TSpreadSheet.SetScreenRowStop(NewRow:Integer);
  4591. { Determines the starting and ending rows when the ending row is known }
  4592. begin
  4593.   ScreenBlock^.Stop.Row := NewRow;
  4594.   FindScreenRowStart;
  4595. end; {...TSpreadSheet.SetScreenRowStop }
  4596.  
  4597.  
  4598. procedure TSpreadSheet.SetState(AState: Word; Enable: Boolean);
  4599. { Changes the state of the spreadsheet and displays or hides the cursor
  4600.   depending on whether the spreadsheet is activated or deactivated }
  4601. begin
  4602.   if AState = sfActive then
  4603.   begin
  4604.     SetProtection(SheetProtected, False);
  4605.     if Enable then
  4606.       begin
  4607.         CurrPos := OldCurrPos;
  4608.         if ScreenBlock^.CellInBlock(CurrPos) then
  4609.           DisplayCell(CurrPos);
  4610.       end {...if Enable }
  4611.     else
  4612.       begin
  4613.         OldCurrPos := CurrPos;
  4614.         CurrPos.Col := Succ(ScreenBlock^.Stop.Col);
  4615.         CurrPos.Row := Succ(ScreenBlock^.Stop.Row);
  4616.         if ScreenBlock^.CellInBlock(OldCurrPos) then
  4617.           DisplayCell(OldCurrPos);
  4618.       end; {...if/else }
  4619.   end; {...if AState = sfActive }
  4620.   TScroller.SetState(AState, Enable);
  4621. end; {...TSpreadSheet.SetState }
  4622.  
  4623.  
  4624. procedure TSpreadSheet.SetUnlocked;
  4625. { Mark the cell or group of cells as unlocked, allowing the modification of
  4626.   the cells' contents even when the sheet is protected }
  4627. begin
  4628.   if BlockOn then
  4629.     UnlockedHash.Add(CurrBlock^.Start, CurrBlock^.Stop)
  4630.   else
  4631.     UnlockedHash.Add(CurrPos, CurrPos);
  4632.   DisplayCellData;
  4633. end; {...TSpreadSheet.SetUnlocked }
  4634.  
  4635.  
  4636. procedure TSpreadSheet.SortData;
  4637. { Sorts the data in the current block using up to three different keys }
  4638. var
  4639.   Dialog : PDialog;
  4640.   Block : TBlock;   { Block of data that will be sorted }
  4641.   Pos : CellPos;    { Used only to complete parameter list }
  4642.   F : File;
  4643.  
  4644.     function SortOrder(CheckBoxItem: Byte): SortTypes;
  4645.     { Returns the sort type value corresponding to the checkbox item selected }
  4646.     begin
  4647.       if CheckBoxItem = 0 then
  4648.         SortOrder := Ascending
  4649.       else
  4650.         SortOrder := Descending;
  4651.     end; {...SortOrder }
  4652.  
  4653.     function KeyColumn(KeyValue: String): Word;
  4654.     { Returns the corresponding column for the given string }
  4655.     var
  4656.       IndicatorLength: Byte;
  4657.       Pos : CellPos;
  4658.       Indicator : String;
  4659.       Col, FormLen : Word;
  4660.     begin
  4661.       Col := 0;
  4662.       IndicatorLength := Length(GLStringList^.Get(sColumnEntryIndicator)+' ');
  4663.       Indicator := Copy(KeyValue, 1, IndicatorLength);
  4664.       if Indicator = (GLStringList^.Get(sColumnEntryIndicator)+' ') then
  4665.       begin
  4666.         Indicator := Copy(KeyValue, Succ(IndicatorLength), (Length(KeyValue) -
  4667.           IndicatorLength));
  4668.         Col := StringToCol(Indicator, MaxCols);
  4669.       end; {...if Indicator = (GLStringList^.Get(sColumnEntryIndicator)+' ') }
  4670.       if Col = 0 then
  4671.         ColHeadersHash.SearchName(KeyValue, Col);
  4672.       KeyColumn := Col;
  4673.     end; {...KeyColumn }
  4674.  
  4675. begin
  4676.   if not BlockOn then
  4677.   begin
  4678.     CurrBlock^.Start.Col := 1;
  4679.     CurrBlock^.Start.Row := 1;
  4680.     CurrBlock^.Stop := LastPos;
  4681.   end; {...if not BlockOn }
  4682.   Move(CurrBlock^, Block, SizeOf(CurrBlock^));
  4683.   Dialog := PDialog(GLResFile^.Get('SortDialog'));
  4684.   if Application^.ValidView(Dialog) <> NIL then
  4685.   begin
  4686.     if Desktop^.ExecView(Dialog) <> cmCancel then
  4687.     begin
  4688.       Dialog^.GetData(RSortInfo);
  4689.       Dispose(Dialog, Done);
  4690.       Dialog := PDialog(GLResFile^.Get('SortingDialog'));
  4691.       if Application^.ValidView(Dialog) <> NIL then
  4692.       begin
  4693.         Desktop^.Insert(Dialog);
  4694.         StatusLine^.Update;
  4695.         with RSortInfo do
  4696.         begin
  4697.           SortObject^.Init(@CellHash);
  4698.           SortObject^.Sort(Block,
  4699.           KeyColumn(FirstKey), SortOrder(FirstKeyOrder),
  4700.           KeyColumn(SecondKey), SortOrder(SecondKeyOrder),
  4701.           KeyColumn(ThirdKey), SortOrder(ThirdKeyOrder));
  4702.         end; {...with RSortInfo }
  4703.         Desktop^.Delete(Dialog);
  4704.         Dispose(Dialog, Done);
  4705.         Dialog := PDialog(GLResFile^.Get('UpdatingTablesDialog'));
  4706.         Desktop^.Insert(Dialog);
  4707.         StoreTablesToTempFile;
  4708.         DoneHashTables;
  4709.         Pos.Col := 0;
  4710.         Pos.Row := 0;
  4711.         LoadTablesFromTempFile(Pos, 0, 0);
  4712.         Assign(F, GLStringList^.Get(sTempFileName));
  4713.         Erase(F);
  4714.         FixOverwrite;
  4715.         DisplayAllCells;
  4716.         DisplayCellData;
  4717.         Desktop^.Delete(Dialog);
  4718.       end {...if Application^.ValidView(Dialog) <> NIL }
  4719.     end; {...if ExecView(Dialog) <> cmCancel }
  4720.     if Dialog <> NIL then
  4721.       Dispose(Dialog, Done);
  4722.   end; {...if Application^.ValidView(Dialog) <> NIL }
  4723. end; {...TSpreadSheet.SortData }
  4724.  
  4725.  
  4726. function TSpreadSheet.SortObject : PSortObject;
  4727. { Returns a pointer to the sort object to be used }
  4728. begin
  4729.   SortObject := StandardSortObject;
  4730. end; {...TSpreadSheet.SortObject }
  4731.  
  4732.  
  4733. procedure TSpreadSheet.Store(var S: TStream);
  4734. { Writes the spreadsheet object to a stream }
  4735. const
  4736.   FileHeader : String[Length(OOGridFileHeader)] = OOGridFileHeader;
  4737. begin
  4738.   TScroller.Store(S);
  4739.   S.Write(FileHeader, SizeOf(FileHeader));
  4740.   S.Write(EmptyRowsAtTop, SizeOf(EmptyRowsAtTop));
  4741.   S.Write(EmptyRowsAtBottom, SizeOf(EmptyRowsAtBottom));
  4742.   S.Write(MaxCols, Sizeof(MaxCols));
  4743.   S.Write(MaxRows, SizeOf(MaxRows));
  4744.   S.Write(DefaultColWidth, SizeOf(DefaultColWidth));
  4745.   S.Write(DefaultDecimalPlaces, SizeOf(DefaultDecimalPlaces));
  4746.   S.Write(MaxDecimalPlaces, SizeOf(MaxDecimalPlaces));
  4747.   S.Write(DefaultCurrency, SizeOf(DefaultCurrency));
  4748.   S.Write(LastPos, SizeOf(LastPos));
  4749.   StoreHashTables(S);
  4750.   S.Put(ScreenBlock);
  4751.   S.Write(CurrPos, SizeOf(CurrPos));
  4752.   S.Write(BlockOn, SizeOf(BlockOn));
  4753.   S.Put(CurrBlock);
  4754.   S.Write(DisplayFormulas, SizeOf(DisplayFormulas));
  4755.   S.Write(AutoCalc, SizeOf(AutoCalc));
  4756.   S.Write(DisplayHeaders, SizeOf(DisplayHeaders));
  4757.   S.Write(SheetProtected, SizeOf(SheetProtected));
  4758.   SetChanged(ModifiedNo);
  4759. end; {...TSpreadSheet.Store }
  4760.  
  4761.  
  4762. procedure TSpreadSheet.StoreHashTables(var S: TStream);
  4763. { Stores the hash tables in a stream }
  4764. begin
  4765.   S.Write(CellHash.Items, SizeOf(CellHash.Items));
  4766.   S.Write(WidthHash.Items, 2);
  4767.   S.Write(FormatHash.Items, SizeOf(FormatHash.Items));
  4768.   S.Write(ColHeadersHash.Items, 2);
  4769.   S.Write(UnlockedHash.Items, SizeOf(UnlockedHash.Items));
  4770.   CellHash.Store(S);
  4771.   WidthHash.Store(S);
  4772.   FormatHash.Store(S);
  4773.   ColHeadersHash.Store(S);
  4774.   UnlockedHash.Store(S);
  4775. end; {...TSpreadSheet.StoreHashTables }
  4776.  
  4777.  
  4778. procedure TSpreadSheet.StoreTablesToTempFile;
  4779. { Stores the hash tables in a temporary file in disk }
  4780. var
  4781.   S : TBufStream;
  4782. begin
  4783.   S.Init(GLStringList^.Get(sTempFileName), stCreate, 1024);
  4784.   StoreHashTables(S);
  4785.   S.Done;
  4786. end; {...TSpreadSheet.StoreTablesToTempFile }
  4787.  
  4788.  
  4789. procedure TSpreadSheet.ToggleAutoCalc;
  4790. { Turns the autocalc mode on and off }
  4791. begin
  4792.   if AutoCalc then
  4793.     begin
  4794.       AutoCalc := False;
  4795.       DisplayInfo;
  4796.     end {...if AutoCalc }
  4797.   else
  4798.     begin
  4799.       AutoCalc := True;
  4800.       DisplayInfo;
  4801.       Recalc(DisplayYes);
  4802.     end; {...if/else }
  4803. end; {...TSpreadSheet.ToggleAutoCalc }
  4804.  
  4805.  
  4806. procedure TSpreadSheet.ToggleBlockOn;
  4807. { Turns the block state on }
  4808. begin
  4809.   if not BlockOn then
  4810.   begin
  4811.     BlockOn := True;
  4812.     CurrBlock^.Init(CurrPos);
  4813.     DisplayInfo;
  4814.   end {...if not BlockOn }
  4815. end; {...TSpreadSheet.ToggleBlockOn }
  4816.  
  4817.  
  4818. procedure TSpreadSheet.ToggleDisplayHeaders;
  4819. { Toggles between displaying and not displaying the column names }
  4820. begin
  4821.   DisplayHeaders := not DisplayHeaders;
  4822.   DisplayCols;
  4823.   DisplayInfo;
  4824. end; {...TSpreadSheet.ToggleDisplayHeaders }
  4825.  
  4826.  
  4827. procedure TSpreadSheet.ToggleEnd;
  4828. { Toggles on and off the Go_To_End status (the END key was pressed) }
  4829. begin
  4830.   GoToEnd := Not GoToEnd;
  4831.   DisplayInfo;
  4832. end; {...TSpreadSheet.ToggleEnd }
  4833.  
  4834.  
  4835. procedure TSpreadSheet.ToggleFormulaDisplay;
  4836. { Toggles between displaying the cell formulas or their values }
  4837. var
  4838.   OChanged : Boolean;
  4839.   CP : PCell;
  4840. begin
  4841.   Desktop^.Lock;
  4842.   DisplayFormulas := not DisplayFormulas;
  4843.   DisplayInfo;
  4844.   OChanged := True;
  4845.   with CellHash do
  4846.   begin
  4847.     CP := FirstItem;
  4848.     while (CP <> NIL) and OChanged do
  4849.     begin
  4850.       if CP^.ShouldUpdate then
  4851.         OChanged := OverwriteHash.Change(CP, CP^.Overwritten(CellHash,
  4852.           FormatHash, WidthHash, LastPos, MaxCols, GetColWidth,
  4853.           DisplayFormulas));
  4854.         CP := NextItem;
  4855.     end; {...while (CP <> NIL) and OChanged }
  4856.   end; {...with CellHash }
  4857.   DisplayAllCells;
  4858.   DisplayCellData;
  4859.   Desktop^.Unlock;
  4860. end; {...TSpreadSheet.ToggleFormulaDisplay }
  4861.  
  4862.  
  4863. function TSpreadSheet.TrackCursor: Boolean;
  4864. { Checks if the cursor is within the limits of the currently displayed
  4865.   screen block.  If not, it adjust the screen block to include
  4866.   the position of the cursor. }
  4867. begin
  4868.   TrackCursor := False;
  4869.   if CurrPos.Col < ScreenBlock^.Start.Col then
  4870.     begin
  4871.       SetScreenColStart(CurrPos.Col);
  4872.       TrackCursor := True;
  4873.     end {...if CurrPos.Col < ScreenBlock^.Start.Col }
  4874.   else if CurrPos.Col > ScreenBlock^.Stop.Col then
  4875.     begin
  4876.       SetScreenColStop(CurrPos.Col);
  4877.       TrackCursor := True;
  4878.     end; {...else if CurrPos.Col > ScreenBlock^.Stop.Col }
  4879.   if CurrPos.Row < ScreenBlock^.Start.Row then
  4880.     begin
  4881.       SetScreenRowStart(CurrPos.Row);
  4882.       TrackCursor := True;
  4883.     end {...if CurrPos.Row < ScreenBlock^.Start.Row }
  4884.   else if CurrPos.Row > ScreenBlock^.Stop.Row then
  4885.     begin
  4886.       SetScreenRowStop(CurrPos.Row);
  4887.       TrackCursor := True;
  4888.     end; {...else if CurrPos.Row > ScreenBlock^.Stop.Row }
  4889. end; {...TSpreadSheet.TrackCursor }
  4890.  
  4891.  
  4892. procedure TSpreadSheet.UpdateScreenBlockDisplay;
  4893. { Displays the screen and changes the scrollbars' values whenever the
  4894.   screen block was changed }
  4895. begin
  4896.   ExtendCurrBlock(RedrawNo);
  4897.   HScrollBar^.Value := ScreenBlock^.Start.Col;
  4898.   HScrollBar^.Drawview;
  4899.   VScrollBar^.Value := ScreenBlock^.Start.Row;
  4900.   VScrollBar^.Drawview;
  4901.   DrawView;
  4902. end; {...TSpreadSheet.UpdateScreenBlockDisplay }
  4903.  
  4904.  
  4905. function TSpreadSheet.WidthHashStart:BucketRange;
  4906. { Returns the number of initial buckets of the Width hash table }
  4907. begin
  4908.   WidthHashStart := 10;
  4909. end; {...TSpreadSheet.WidthHashStart }
  4910.  
  4911.  
  4912. function TSpreadSheet.XToCol(X: Byte): Integer;
  4913. { Returns the spreadsheet column a particular screen column position is in }
  4914. var
  4915.   ColScrPos : Byte;
  4916.   Counter   : Integer;
  4917. begin
  4918.   XToCol := 0;
  4919.   with ScreenBlock^ do
  4920.   begin
  4921.     for Counter := Start.Col to Stop.Col do
  4922.     begin
  4923.       ColScrPos := ColToX(Counter);
  4924.       if (X < (ColScrPos + ColWidth(Counter))) and (X >= ColScrPos) then
  4925.         XToCol := Counter;
  4926.     end; {...for Counter }
  4927.   end; {...with ScreenBlock^ }
  4928. end; {...TSpreadSheet.XToCol }
  4929.  
  4930.  
  4931. function TSpreadSheet.YToRow(Y: Byte): Integer;
  4932. { Returns the spreadsheet row a particular screen row position is in }
  4933. begin
  4934.   YToRow := ((Y - DisplayArea.UpperLeft.Row) + ScreenBlock^.Start.Row);
  4935. end; {...TSpreadSheet.YToRow }
  4936.  
  4937.  
  4938. procedure TSpreadSheet.DoneHashTables;
  4939. { Disposes all the hash tables }
  4940. var
  4941.   Block : TBlock;
  4942.   Deleted : Boolean;
  4943. begin
  4944.   Block.Init(LastPos);
  4945.   Block.Start.Col := 1;
  4946.   Block.Start.Row := 1;
  4947.   DeleteBlock(Block, Deleted);
  4948.   CellHash.Done;
  4949.   WidthHash.Done;
  4950.   FormatHash.Done;
  4951.   OverWriteHash.Done;
  4952.   ColHeadersHash.Done;
  4953.   UnlockedHash.Done;
  4954. end; {...TSpreadSheet.DoneHashTables }
  4955.  
  4956. destructor TSpreadSheet.Done;
  4957. { Disposes the spreadsheet }
  4958. begin
  4959.   if ColStart <> NIL then
  4960.     FreeMem(ColStart, MaxScreenCols);
  4961.   if ScreenBlock <> NIL then
  4962.     Dispose(ScreenBlock, Done);
  4963.   if CurrBlock <> NIL then
  4964.     Dispose(CurrBlock, Done);
  4965.   DoneHashTables;
  4966.   TScroller.Done;
  4967. end; {...TSpreadSheet.Done }
  4968.  
  4969. begin
  4970.   ClipBoard.BlockToCopy := NIL;
  4971.   InitClipBoard;
  4972.   with PrinterConfigRec do
  4973.   begin
  4974.     PrinterCondensedOnCode := DefaultPrinterCondensedOnCode;
  4975.     PrinterCondensedOffCode := DefaultPrinterCondensedOffCode;
  4976.     PrinterUnderlineOnCode := DefaultPrinterUnderlineOnCode;
  4977.     PrinterUnderlineOffCode := DefaultPrinterUnderlineOffCode;
  4978.     PrinterBoldOnCode := DefaultPrinterBoldOnCode;
  4979.     PrinterBoldOffCode := DefaultPrinterBoldOffCode;
  4980.   end; {...with PrinterConfigRec }
  4981.   with RPrint do
  4982.   begin
  4983.     PrintTo := 0;
  4984.     PrintSize := 0;
  4985.     PrintRows := 0;
  4986.     PrintColumns := 0;
  4987.     TopMargin := DefaultTopMargin;
  4988.     BottomMargin := DefaultBottomMargin;
  4989.     LeftMargin := DefaultLeftMargin;
  4990.     RightMargin := DefaultRightMargin;
  4991.     Other := 0;
  4992.     PageRows := DefaultPageRows;
  4993.     NormalCols := DefaultNormalCols;
  4994.     CondensedCols := DefaultCondensedCols;
  4995.   end; {...with RPrint }
  4996. end. {...GLTSheet unit }